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
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;
False/False
, like the mono compiled versionFalse/True
, like the MS compiled versionFor 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
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...
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).