Unexpected behavior in c# generic method on .Equals

前端 未结 3 1745
孤独总比滥情好
孤独总比滥情好 2021-02-13 09:04

Why does the Equals method return a different result from within the generic method? I think that there\'s some automatic boxing here that I don\'t understand.

Here\'s a

相关标签:
3条回答
  • 2021-02-13 10:00

    FWIW, on mono 2.8+ both return values are False, outputting

    False
    False
    

    Amazingly, csc.exe from VS2010 produces different results, indeed, outputting:

    False
    True
    

    Even more interestingly, the problem appears not with the generated IL code, but with the Framework implementation/JIT engine;

    • executing the MS-compiled image with the Mono VM results in False/False, like the mono compiled version
    • executing the Mono-compiled image with the MS VM results in False/True, like the MS compiled version

    For your interest, here are the disassemblies of Microsoft's CSC.exe compiler (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
    

    and Mono's gmcs.exe compiler (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
    
    0 讨论(0)
  • 2021-02-13 10:02

    TimezoneInfo defines it's own overload of Equals(TimeZoneInfo). In the Compare method, the object equals is used (it's a virtual method call of Object.Equals), whereas in Console.WriteLine(tzOne.Equals(tzTwo)) the overloaded (new) TimeZoneInfo.Equals method is called.

    TimeZoneInfo obviously hasn't overridden the Object.Equals method correctly...

    0 讨论(0)
  • 2021-02-13 10:03

    TimeZoneInfo does not override the Object Equals method, so it calls the default Object Equals, which apparently does not work as expected. I would consider this a bug in TimeZoneInfo. This should work:

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

    The above will cause it to call Equals<T>, which is the method you were calling above (it implicitly preferred the generic call because it was more specific to the parameter type than the Object Equals; inside the generic method, however, it had no way to be sure that such a generic Equals existed, since there was no constraint guaranteeing this).

    0 讨论(0)
提交回复
热议问题