45

Is it possible in C# to compare two objects of unknown types, (including both reference and value types) using their type comparators if they exist?

The goal is to write a function that would have a signature like this:

public bool Compare(object a, object b)
{
     // compare logic goes here
}

Which would return

Compare(100d, 100d) == true
Compare(100f, 100f) == true
Compare("hello", "hello") == true
Compare(null, null) == true 
Compare(100d, 101d) == false
Compare(100f, null) == false

// Use type comparators where possible, i.e.:
Compare(new DateTime(2010, 12, 01), new DateTime(2010, 12, 01)) == true
Compare(new DateTime(2010, 12, 01), new DateTime(2010, 12, 02)) == false
Compare(new DateTime(2010, 12, 01), null) == false

Is there a generic approach to solving this problem that would work for any type of object?

Duane
  • 670
  • 1
  • 6
  • 9

5 Answers5

72

You can use the static object.Equals(object x, object y) method and not bother writing your method at all. That will handle nulls appropriately, and delegate to an implementation of object.Equals(object) associated with either x or y... it shouldn't matter which, as Equals is meant to be symmetric.

Note that this doesn't use the == operators for any type - operators can't be overridden, only overloaded (which means they're chosen at compile-time, not execution-time. In most cases Equals should do what you want. In some cases, == may not be overloaded even though Equals is overridden... but I've never known the reverse to be true in any types I've worked with.

Note that using this approach will box any value types...

EDIT: Removed section about effectively reimplementing - poorly - EqualityComparer<T>.Default. See Marc's answer for more. This won't help you if you can't use a generic type, of course.

One final point: I wouldn't call your method Compare. That name is usually associated with ordering values rather than comparing them for equality.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Surely the latter will only work for reference types as you will have to have `where T : class` in order to use `v1 == null` so preventing value type comparision. – aqwert Dec 02 '10 at 07:52
  • Any reason not to use `EqualityComparer.Default` here? That would make things more general (`IEquatable` constraint not required), but not force boxing unless absolutely necessary, wouldn't it? – Ani Dec 02 '10 at 08:25
  • @aqwert: Nope. It just always evaluates to null for value types. – Jon Skeet Dec 02 '10 at 08:27
  • @Ani: Merely a brain freeze :) Marc has now covered that in his answer, so I've edited my stuff out. – Jon Skeet Dec 02 '10 at 08:30
  • Is there a way to make this also work for primitive types such as int, long, etc. – Mensur Apr 09 '13 at 05:08
  • @Mensur: It will already work for primitive types. Just use `EqualityComparer.Default` and all will be fine. – Jon Skeet Apr 09 '13 at 05:42
  • Equals is meant to be symmetric? This seems not be guaranteed: `2.Equals(2.0)` vs `2.0.Equals(2)` ... Good grief! How to get there everywhere? :( – KnorxThieus Aug 07 '17 at 13:19
21

A reasonable option is is to work with generics, i.e.

public bool Compare<T>(T a, T b) {...}

You won't need to specify the T in your code, as the compiler will usually be able to figure it out (i.e. your existing samples would work "as-is")

For the implementation:

bool equal = EqualityComparer<T>.Default.Equals(x, y);

(for generic type T)

but actually I would avoid the word Compare, as that is used elsewhere to mean < / == / > - so I might have:

public static bool Equals<T>(T a, T b) {
    return EqualityComparer<T>.Default.Equals(a, b);
}

This:

  • avoids boxing when T is a struct
  • handles nulls / Nullable<T> correctly
  • supports IEquatable<T>
  • or falls back to regular Equals

it does not use the == operator, but MiscUtil has an Operator class that will, via

bool equal = Operator.Equal<T>(x,y);

(note this latter will fail if T doesn't have an == operator, although thinking about it, it could use EqualityComparer<T>.Default.Equals as a fallback; it just doesn't)


For completeness, note that Comparer<T>.Default.Compare(x,y) handles comparison operations.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • 1
    Thanks for the excellent answer, both your own and Jon Skeet's answers work for my particular use case. – Duane Dec 03 '10 at 02:45
6

How about object.equals(x, y)? This will accept null values as well.

BlueVoodoo
  • 3,626
  • 5
  • 29
  • 37
0

What about

if (a == null && b == null) return true;
if (a == null && b != null) return false;
return (a.equals(b));

?

Zeemee
  • 10,486
  • 14
  • 51
  • 81
-4

I'm not 100% sure if this works in all cases, but give it a try

public bool Compare(object a, object b)
{
    return a.Equals(b);
}
KBoek
  • 5,794
  • 5
  • 32
  • 49