Reputation: 466
I currently have two objects (of the same type) that may represent any primitive value such as string, int, datetime etc.
var valueX = ...;
var valueY = ...;
Atm I compare them on string level like this
var result = string.Compare(fieldValueX.ToString(), fieldValueY.ToString(), StringComparison.Ordinal);
But I need to compare them on type level (as ints if those happen to be ints
int i = 0;
int j = 2;
i.CompareTo(j);
, as dates if they happen to be date etc), something like
object.Compare(x,y);
That returns -1,0,1 in the same way. What are the ways to achieve that ?
Upvotes: 3
Views: 43842
Reputation: 2200
converting the objects to dictionary, then following math set(s) concept subtract them, result items should be empty in case they are identically.
public static IDictionary<string, object> ToDictionary(this object source)
{
var fields = source.GetType().GetFields(
BindingFlags.GetField |
BindingFlags.Public |
BindingFlags.Instance).ToDictionary
(
propInfo => propInfo.Name,
propInfo => propInfo.GetValue(source) ?? string.Empty
);
var properties = source.GetType().GetProperties(
BindingFlags.GetField |
BindingFlags.GetProperty |
BindingFlags.Public |
BindingFlags.Instance).ToDictionary
(
propInfo => propInfo.Name,
propInfo => propInfo.GetValue(source, null) ?? string.Empty
);
return fields.Concat(properties).ToDictionary(key => key.Key, value => value.Value); ;
}
public static bool EqualsByValue(this object source, object destination)
{
var firstDic = source.ToFlattenDictionary();
var secondDic = destination.ToFlattenDictionary();
if (firstDic.Count != secondDic.Count)
return false;
if (firstDic.Keys.Except(secondDic.Keys).Any())
return false;
if (secondDic.Keys.Except(firstDic.Keys).Any())
return false;
return firstDic.All(pair =>
pair.Value.ToString().Equals(secondDic[pair.Key].ToString())
);
}
public static bool IsAnonymousType(this object instance)
{
if (instance == null)
return false;
return instance.GetType().Namespace == null;
}
public static IDictionary<string, object> ToFlattenDictionary(this object source, string parentPropertyKey = null, IDictionary<string, object> parentPropertyValue = null)
{
var propsDic = parentPropertyValue ?? new Dictionary<string, object>();
foreach (var item in source.ToDictionary())
{
var key = string.IsNullOrEmpty(parentPropertyKey) ? item.Key : $"{parentPropertyKey}.{item.Key}";
if (item.Value.IsAnonymousType())
return item.Value.ToFlattenDictionary(key, propsDic);
else
propsDic.Add(key, item.Value);
}
return propsDic;
}
originalObj.EqualsByValue(messageBody); // will compare values.
Upvotes: 0
Reputation: 182
Object1.Equals(obj1, obj2) wont work unless @object is referencing the same object.
EG:
var obj1 = new MyObject();
var obj2 = new MyObject();
This will return "False" for Object1.Equals(obj1, obj2)
as they are different ref's
var obj1 = new MyObject();
var obj2 = obj1;
This will return "True" for Object1.Equals(obj1, obj2)
as they are the same ref.
Solution: You will most likely need to write an extension method that overrides Object.Equals. either create a custom object comparer for a specific type (See here for custom object comparer:) or you can dynamically go through each property and compare.
Upvotes: 2
Reputation: 466
Thanks for your answers, the correct way was to check if the object implements IComparable and if it does - make a typecast and call CompareTo
if (valueX is IComparable)
{
var compareResult = ((IComparable)valueX).CompareTo((IComparable)valueY);
}
Upvotes: 11
Reputation: 3833
You can write a GeneralComparer
with a Compare
method, overloaded as necessary.
For types that must perform a standard comparison you can use EqualityComparer<T>.Default
; for other types you write your own comparison function. Here's a sample:
static class GeneralComparer
{
public static int Compare(int x, int y)
{
//for int, use the standard comparison:
return EqualityComparer<int>.Default.Equals(x, y);
}
public static int Compare(string x, string y)
{
//for string, use custom comparison:
return string.Compare(x, y, StringComparison.Ordinal);
}
//overload for DateTime
//overload for MyType
//overload for object
//...
}
The correct overload is chosen at runtime. There's a drawback: if you declare two int (or other specific types) as object, the object overload is called:
object a = 2;
object b = 3;
//this will call the "Compare(object x, object y)" overload!
int comparison = GeneralComparer.Compare(a, b);
Upvotes: 0
Reputation: 591
There's several options to do this.
Override Object.Equal
You can override the Object.Equal() method in the class, and then determine what makes the objects equal there. This can also let you cleverly decide what to compare, since it appears those objects can be multiple data types. Inside this override, you'll need to handle each possible case. You can read more about this option here:
https://msdn.microsoft.com/en-us/library/bsc2ak47(v=vs.110).aspx
It should be noted by default, Object.Equal() will compare your objects references.
Implement IComparable
IComparable is a neat interface that gives an object Compare
. As the comments mention, this will let you define how to compare the objects based on whatever criteria you want.
This option gets covered here: https://msdn.microsoft.com/en-us/library/system.icomparable(v=vs.110).aspx
Implement CompareBy() Methods
Alternatively, you can implement methods for each possible type, ie CompareByInt()
or CompareByString()
, but this method depends on you knowing what you're going to have when you go to do it. This will also have the negative effect of making code more difficult to maintain, as there's many more methods involved.
Upvotes: 0