.Equals中c#泛型方法中的意外行为

时间:2011-11-21 21:24:45

标签: c# generics .net-4.0

为什么Equals方法从泛型方法中返回不同的结果?我认为这里有一些我不明白的自动拳击。

以下是使用.net 3.5或4.0再现行为的示例:

static void Main(string[] args)
{
    TimeZoneInfo tzOne = TimeZoneInfo.Local;
    TimeZoneInfo tzTwo = TimeZoneInfo.FindSystemTimeZoneById(tzOne.StandardName);
    Console.WriteLine(Compare(tzOne, tzTwo));
    Console.WriteLine(tzOne.Equals(tzTwo));
}

private static Boolean Compare<T>(T x, T y)
{
    if (x != null)
    {
        return x.Equals(y);
    }
    return y == null;
}

输出:

False
True

编辑:此代码可以按照需要运行,而不会有太多妥协:

private static Boolean Compare<T>(T x, T y)
{
    if (x != null)
    {
        if (x is IEquatable<T>)
        {
            return (x as IEquatable<T>).Equals(y);
        }
        return x.Equals(y);
    }
    return y == null;
}

跟进:我提交了一个bug via MS Connect,它已被解决为已修复,因此可能会在下一版本的.net框架中修复此问题。如果可用,我会更新更多细节。

PS :这似乎在.net 4.0及更高版本中得到修复(通过查看mscorlib中反汇编的TimeZoneInfo)。

3 个答案:

答案 0 :(得分:13)

TimeZoneInfo不会覆盖Object Equals方法,因此它会调用默认的Object Equals,这显然不能按预期工作。我会认为这是TimeZoneInfo中的一个错误。这应该有效:

private static Boolean Compare<T>(T x, T y)
        where T: IEquatable<T>
{
    if (x != null)
    {
        return x.Equals(y);
    }
    return false;
}

以上将导致它调用Equals<T>,这是您在上面调用的方法(它隐式优先选择泛型调用,因为它比参数类型更具体于Object Equals;在泛型方法中,但是,它没有办法确定存在这样的通用等式,因为没有约束保证这一点。)

答案 1 :(得分:9)

FWIW,在单声道2.8+上,两个返回值均为False,输出

False
False

令人惊讶的是,来自VS2010的csc.exe会产生不同的结果,实际上是输出:

False
True

更有趣的是,问题与生成的IL代码,但是使用Framework实现/ JIT引擎;

  • 使用Mono VM执行MS编译的图像会产生False/False,就像单声道编译版本一样
  • 使用MS VM执行Mono编译的图像会产生False/True,就像MS编译版本一样

为了您的兴趣,以下是Microsoft的CSC.exe编译器(csc.exe /optimize+ test.cs)的反汇编:

.method private static hidebysig 
       default bool Compare<T> (!!T x, !!T y)  cil managed 
{
    // Method begins at RVA 0x2087
// Code size 30 (0x1e)
.maxstack 8
IL_0000:  ldarg.0 
IL_0001:  box !!0
IL_0006:  brfalse.s IL_001c

IL_0008:  ldarga.s 0
IL_000a:  ldarg.1 
IL_000b:  box !!0
IL_0010:  constrained. !!0
IL_0016:  callvirt instance bool object::Equals(object)
IL_001b:  ret 
IL_001c:  ldc.i4.0 
IL_001d:  ret 
} // end of method Program::Compare

和Mono的gmcs.exe编译器(dmcs -optimize+ test.cs):

.method private static hidebysig 
       default bool Compare<T> (!!T x, !!T y)  cil managed 
{
    // Method begins at RVA 0x212c
// Code size 33 (0x21)
.maxstack 4
IL_0000:  ldarg.0 
IL_0001:  box !!0
IL_0006:  brfalse IL_001f

IL_000b:  ldarga.s 0
IL_000d:  ldarg.1 
IL_000e:  box !!0
IL_0013:  constrained. !!0
IL_0019:  callvirt instance bool object::Equals(object)
IL_001e:  ret 
IL_001f:  ldc.i4.0 
IL_0020:  ret 
} // end of method Program::Compare

答案 2 :(得分:3)

TimezoneInfo定义它自己的Equals(TimeZoneInfo)重载。在Compare方法中,使用了对象equals(它是Object.Equals的虚方法调用),而在Console.WriteLine(tzOne.Equals(tzTwo)中)调用了重载的(新的)TimeZoneInfo.Equals方法。

TimeZoneInfo显然没有正确覆盖Object.Equals方法......