2

Say I have a generic method in C# that accepts two values of type T:

public void M<T>(T a, T b)
{
    ...
}

Inside body of M() I wish to compare both input values for equality. Since I don't know anything about their run-time types except that they are the same type, I could do this using the object.Equals() static method and let it choose the best way:

public void M<T>(T a, T b)
{
    if (object.Equals(a, b))
    {
        ...
    }
    else
    {
        ...
    }
}

The problem I see here is needless boxing of the two values when T isn't a reference type. I would like to avoid that penalty, because M() is called very frequently.

My question is: is there a better way to go about this? I'm obviously interested in a solution that wouldn't involve too much analysis of T up front, which would offset gains from boxing evasion.

TIA.

aoven
  • 2,248
  • 2
  • 25
  • 36
  • 1
    possible duplicate of [c# compare two generic values](http://stackoverflow.com/questions/488250/c-compare-two-generic-values) – Ed S. Jul 04 '11 at 11:03

1 Answers1

7
if(EqualityComparer<T>.Default.Equals(a,b))
{...}

this uses IEquatable<T> when available to avoid boxing, and handles value-types, reference-types, and "lifted" usage against Nullable<T> to avoid boxing in almost all scenarios.

When IEquatable<T> is not available, it must defer to object.Equals, so boxing of value-types may occur.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • Perfect! I knew about this class, but wasn't really able to connect the dots since I rarely use it in my own code. Thanks, Marc! – aoven Jul 04 '11 at 11:07
  • I think I just found [boxing still happens with `IEquatable<> valuetypes` on mono (trunk)](https://github.com/mono/mono/blob/648425ebe1af72b062b1810e86ada260be9104f0/mcs/class/corlib/System.Collections.Generic/EqualityComparer.cs); I added [a comment to JbEvain's latest commit](https://github.com/mono/mono/commit/648425ebe1af72b062b1810e86ada260be9104f0#commitcomment-460052) there to ask his opinion (I only spent a few minutes looking at monodis output here) – sehe Jul 04 '11 at 12:30
  • I would expect that running EqualityComparer.Default once is usually going to take longer than using instance.Compare once, even in cases where the latter would require boxing. The advantage of using EqualityComparer.Default comes if one can use it once and then use the returned method on hundreds or thousands of value-type instances. Note that only value types and *sealed* class types should implement IEquatable; if I had my druthers, EqualityComparer.Default would check for implementation of IDontUseEquatable and, for any class implementing it, use Object.Equals. – supercat Sep 01 '11 at 20:07
  • @supercat the strategy per `T` is cached in a field, so once it has been used *once*, all subsequent uses of `.Default` are pretty quick. It doesn't have to do any thinking - just a deference... but yes, capturing the return value into a variable is even better. – Marc Gravell Sep 01 '11 at 20:11
  • @Marc Gravell: Thanks for the info. BTW, do you agree that inheritable types shouldn't implement IEquatable? – supercat Sep 01 '11 at 20:54