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

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

这是一个使用.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 x, T y) { if (x != null) { return x.Equals(y); } return y == null; } 

输出:

 False True 

编辑:此代码按预期工作,没有多少妥协:

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

后续 :我通过MS Connect提交了一个错误 ,它已被解决为已修复,因此可能会在下一版本的.net框架中修复。 如果可用,我会更新更多细节。

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

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

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

以上将导致它调用Equals ,这是你上面调用的方法(它隐含地优先使用generics调用,因为它比参数类型更具体于参数类型;但是在generics方法中,它有无法确定存在这样的通用Equals,因为没有约束保证这一点)。

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 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 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 

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

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