这是带有注释的示例:
class Program { // first version of structure public struct D1 { public double d; public int f; } // during some changes in code then we got D2 from D1 // Field f type became double while it was int before public struct D2 { public double d; public double f; } static void Main(string[] args) { // Scenario with the first version D1 a = new D1(); D1 b = new D1(); a.f = b.f = 1; a.d = 0.0; b.d = -0.0; bool r1 = a.Equals(b); // gives true, all is ok // The same scenario with the new one D2 c = new D2(); D2 d = new D2(); c.f = d.f = 1; c.d = 0.0; d.d = -0.0; bool r2 = c.Equals(d); // false! this is not the expected result } }
所以,对于这个你有什么想法?
该错误在以下两行System.ValueType:(我进入了参考源)
System.ValueType
if (CanCompareBits(this)) return FastEqualsCheck(thisObj, obj);
(两种方法都是[MethodImpl(MethodImplOptions.InternalCall)])
[MethodImpl(MethodImplOptions.InternalCall)]
当所有字段都是 8 字节宽时,CanCompareBits错误地返回 true,从而导致两个不同但语义相同的值的按位比较。
CanCompareBits
当至少一个字段不是 8 字节宽时,CanCompareBits返回 false,并且代码继续使用反射循环遍历字段并调用Equals每个值,这正确地视为-0.0等于0.0。
Equals
-0.0
0.0
以下是CanCompareBits来自 SSCLI 的来源:
FCIMPL1(FC_BOOL_RET, ValueTypeHelper::CanCompareBits, Object* obj) { WRAPPER_CONTRACT; STATIC_CONTRACT_SO_TOLERANT; _ASSERTE(obj != NULL); MethodTable* mt = obj->GetMethodTable(); FC_RETURN_BOOL(!mt->ContainsPointers() && !mt->IsNotTightlyPacked()); } FCIMPLEND