簡體   English   中英

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

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

為什么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;
}

跟進 :我通過MS Connect提交了一個錯誤 ,它已被解決為已修復,因此可能會在下一版本的.net框架中修復。 如果可用,我會更新更多細節。

PS :這似乎是在.net 4.0及更高版本中修復的(通過查看mscorlib中TimeZoneInfo的反匯編)。

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> ,這是你上面調用的方法(它隱含地優先使用泛型調用,因為它比參數類型更具體於參數類型;但是在泛型方法中,它有無法確定這樣的通用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> (!!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

TimezoneInfo定義它自己的Equals重載(TimeZoneInfo) 在Compare方法中,使用對象equals(它是Object.Equals的虛方法調用),而在Console.WriteLine(tzOne.Equals(tzTwo)中)調用重載(新)TimeZoneInfo.Equals方法。

TimeZoneInfo顯然沒有正確覆蓋Object.Equals方法......

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM