简体   繁体   English

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

[英]Unexpected behavior in c# generic method on .Equals

Why does the Equals method return a different result from within the generic method? 为什么Equals方法从泛型方法中返回不同的结果? I think that there's some automatic boxing here that I don't understand. 我认为这里有一些我不明白的自动拳击。

Here's an example that reproduces the behavior with .net 3.5 or 4.0: 这是一个使用.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;
}

Output: 输出:

False
True

Edit: This code works as desired without many compromises: 编辑:此代码按预期工作,没有多少妥协:

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

Followup : I filed a bug via MS Connect and it has been resolved as fixed, so it's possible this will be fixed in the next version of the .net framework. 跟进 :我通过MS Connect提交了一个错误 ,它已被解决为已修复,因此可能会在下一版本的.net框架中修复。 I'll update with more details if they become available. 如果可用,我会更新更多细节。

PS : This appears to be fixed in .net 4.0 and later (by looking at the disassembly of TimeZoneInfo in mscorlib). PS :这似乎是在.net 4.0及更高版本中修复的(通过查看mscorlib中TimeZoneInfo的反汇编)。

TimeZoneInfo does not override the Object Equals method, so it calls the default Object Equals, which apparently does not work as expected. TimeZoneInfo不会覆盖Object Equals方法,因此它调用默认的Object Equals,这显然不能按预期工作。 I would consider this a bug in TimeZoneInfo. 我会认为这是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). 以上将导致它调用Equals<T> ,这是你上面调用的方法(它隐含地优先使用泛型调用,因为它比参数类型更具体于参数类型;但是在泛型方法中,它有无法确定这样的通用Equals是否存在,因为没有约束保证这一点)。

FWIW, on mono 2.8+ both return values are False, outputting FWIW,在单声道2.8+上,两个返回值均为False,输出

False
False

Amazingly, csc.exe from VS2010 produces different results, indeed, outputting: 令人惊讶的是,来自VS2010的csc.exe产生了不同的结果,实际上,输出:

False
True

Even more interestingly, the problem appears not with the generated IL code , but with the Framework implementation/JIT engine; 更有趣的是,问题在于生成的IL代码 ,而在于Framework实现/ JIT引擎;

  • executing the MS-compiled image with the Mono VM results in False/False , like the mono compiled version 使用Mono VM执行MS编译的图像会导致False/False ,就像单声道编译版本一样
  • executing the Mono-compiled image with the MS VM results in False/True , like the MS compiled version 使用MS VM执行Mono编译的图像会导致False/True ,就像MS编译版本一样

For your interest, here are the disassemblies of Microsoft's CSC.exe compiler ( csc.exe /optimize+ test.cs ): 为了您的兴趣,以下是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

and Mono's gmcs.exe compiler ( dmcs -optimize+ test.cs ): 和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

TimezoneInfo defines it's own overload of Equals(TimeZoneInfo) . TimezoneInfo定义它自己的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. 在Compare方法中,使用对象equals(它是Object.Equals的虚方法调用),而在Console.WriteLine(tzOne.Equals(tzTwo)中)调用重载(新)TimeZoneInfo.Equals方法。

TimeZoneInfo obviously hasn't overridden the Object.Equals method correctly... TimeZoneInfo显然没有正确覆盖Object.Equals方法......

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM