Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EqualityComparer<T>.Default isn't clever enough

I was reading the source code of EqualityComparer<T>.Default and found that it's not so clever. Here is an example:

enum MyEnum : int { A, B }
EqualityComparer<MyEnum>.Default.Equals(MyEnum.A, MyEnum.B)
//is as fast as 
EqualityComparer<int>.Default.Equals(0, 1)

enum AnotherEnum : long { A = 1L, B = 2L }
//is 8x slower than
EqualityComparer<long>.Default.Equals(1L, 2L)

The reason is obvious from the source code of the private method in EqualityComparer.

private static EqualityComparer<T> CreateComparer()
{
    //non-important codes are ignored
    if (c.IsEnum && (Enum.GetUnderlyingType(c) == typeof(int)))
    {
        return (EqualityComparer<T>) RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType) typeof(EnumEqualityComparer<int>), c);
    }
    return new ObjectEqualityComparer<T>();
}

We can see EqualityComparer<int>.Default,EqualityComparer<MyEnum>.Default and EqualityComparer<long>.Default get a wise comparer whose Equals method looks like:

public static bool Equals(int x, int y)
{
    return x == y;  //or return x.Equals(y); here 
                    //I'm not sure, but neither causes boxing
}

public static bool Equals(MyEnum x, MyEnum y)
{
    return x == y;  //it's impossible to use x.Equals(y) here 
                    //because that causes boxing
}

The above two are clever, but EqualityComparer<AnotherEnum>.Default is unlucky, from the method we can see at last it gets a ObjectEqualityComparer<T>(), whose Equals method probably looks like:

public static bool Equals(AnotherEnum x, AnotherEnum y)
{
    return x.Equals(y);   //too bad, the Equals method is from System.Object
                       //and it's not override, boxing here!
                       //that's why it's so slow
}

I think this condition Enum.GetUnderlyingType(c) == typeof(int) is pointless, if the underlying type of an enum is of type int, the method can convert the default comparer of int to this enum. But why can't an enum based on long? It's not so hard i think? Any special reason? Constructing a comparer like x == y isn't so hard for enum, right? Why at last it gives a slow ObjectEqualityComparer<T> for enums(even it works correctly)?

like image 812
Cheng Chen Avatar asked Apr 29 '11 07:04

Cheng Chen


1 Answers

I think that there's simply no compelling reason for the team responsible to add this feature. All features have an implementation cost which includes (among others) the time to document, code and test.

There are a couple of compelling reasons why this particular feature has not been picked over others so far (and will probably never make the cut IMO):

  • It only applies to a very narrow scenario (comparing enums backed by something other than an int, and doing that in some inner loop)
  • There is a very straightforward and discoverable solution if it causes you a problem (write your own comparer)
like image 116
Jon Avatar answered Oct 21 '22 01:10

Jon