MagicbehindValueType.Equals

字號(hào):

在"Effective C#"一書中,Bill Wagner寫到:“在你創(chuàng)建值類型的時(shí)候,一定要重寫ValueType.Equals()”。他主要考慮的是性能問(wèn)題,在ValueType.Equals函數(shù)的默認(rèn)實(shí)現(xiàn)里,需要使用反射枚舉它所有的成員變量,開(kāi)銷是比較大的。
    考試大提示:公共語(yǔ)言運(yùn)行時(shí)針對(duì)“簡(jiǎn)單”值類型作了特殊的優(yōu)化。讓我們來(lái)瞧一瞧。
    這是rotor中ValueType.Equals函數(shù)的實(shí)現(xiàn)("clr\src\bcl\system\valuetype.cs"):
    bool Equals(object obj)
    {
    //Compare type
    //if there are no GC references in this object we can avoid reflection
    //and do a fast memcmp
    if (CanCompareBits(this))
    {
    return FastEqualsCheck(this, obj);
    }
    //Compare using reflection
    }
    這里最重要的兩個(gè)函數(shù)"CanCompareBits"和"FastEqualsCheck"。它們都被標(biāo)記了"[MethodImpl(MethodImplOptions.InternalCall)]"屬性,說(shuō)明它們的實(shí)現(xiàn)是在公共語(yǔ)言運(yùn)行時(shí)的內(nèi)部的。
    通過(guò)瀏覽rotor的源代碼,Examda提示: 可以在"clr\src\vm\comutilnative.cpp"中找到這兩個(gè)函數(shù)的實(shí)現(xiàn)。
    CanCompareBits函數(shù)的注釋寫到:“如果值類型不包含指針或者沒(méi)有填充,將返回true”。而FastEqualsCheck函數(shù)使用"memcmp"進(jìn)行快速的二進(jìn)制比較。
    你會(huì)說(shuō),這個(gè)優(yōu)化實(shí)現(xiàn)很不錯(cuò)啊,我的值類型很簡(jiǎn)單,不用自己重寫Equals函數(shù)了。但是請(qǐng)等一下,你有沒(méi)有發(fā)現(xiàn)CanCompareBits的問(wèn)題?
    問(wèn)題是注釋中提到的條件并不能保證二進(jìn)制的比較能夠得到正確的結(jié)果。
    設(shè)想一下,你有一個(gè)值類型A,它只有一個(gè)float類型的成員f。你定義了兩個(gè)A的對(duì)象a和b,a.f=+0.0,b.f=-0.0。從邏輯上來(lái)講,它們應(yīng)該是相等的,但是它們的二進(jìn)制表示卻是不同的。因此沒(méi)有優(yōu)化的代碼將返回true,而優(yōu)化之后的版本將返回false。
    類似的,如果你的值類型中包含了其它重寫了Equals函數(shù)的值類型,那么上述的優(yōu)化都可能導(dǎo)致錯(cuò)誤的結(jié)果。
    這是公共語(yǔ)言運(yùn)行時(shí)的一個(gè)bug,但是它也告訴我們,你為你的值類型重寫Equals函數(shù)