EqualityComparer< T>.Default不够聪明

本文关键字:不够 Default EqualityComparer | 更新日期: 2023-09-27 17:53:58

我在读EqualityComparer<T>.Default的源代码,发现它不是那么聪明。下面是一个例子:

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)

从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>();
}

我们可以看到EqualityComparer<int>.Default, EqualityComparer<MyEnum>.DefaultEqualityComparer<long>.Default得到一个明智的比较,其Equals方法如下:

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
}

上面两个是聪明的,但是EqualityComparer<AnotherEnum>.Default是不幸的,从方法我们可以看到最后它得到了一个ObjectEqualityComparer<T>(),它的Equals方法可能看起来像:

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
}

我认为这个条件Enum.GetUnderlyingType(c) == typeof(int)是没有意义的,如果枚举的基础类型是int类型,该方法可以 int的默认比较器转换为该枚举。但是为什么不能基于long的枚举呢?我觉得没那么难吧?有什么特别的原因吗?构造像x == y这样的比较器对于enum来说并不难,对吧?为什么最后它给出了一个缓慢的枚举ObjectEqualityComparer<T>(即使它工作正确)?

EqualityComparer< T>.Default不够聪明

我认为对于负责添加此功能的团队来说,根本没有令人信服的理由。所有的特性都有实现成本,其中包括编写文档、编写代码和测试的时间。

有几个令人信服的原因,为什么这个特殊的功能到目前为止还没有被选中(并且可能永远不会被选中):

  • 它只适用于一个非常狭窄的场景(比较enumsint以外的东西支持,并在一些内部循环中这样做)
  • 有一个非常直接和可发现的解决方案,如果它导致你的问题(编写自己的比较器)