eXPerience
eXPerience

Reputation: 466

C# Compare two object values

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

Answers (5)

Mohamed.Abdo
Mohamed.Abdo

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.

source of the code

Upvotes: 0

Tristan van Dam
Tristan van Dam

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

eXPerience
eXPerience

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

Massimiliano Kraus
Massimiliano Kraus

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

Adam Wells
Adam Wells

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

Related Questions