任何人都可以解释这种奇怪的行为与C#中签署的花车?

以下是带评论的示例:

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的以下两行中:(我步入参考源)

if (CanCompareBits(this)) 
    return FastEqualsCheck(thisObj, obj);

(两种方法都是[MethodImpl(MethodImplOptions.InternalCall)]

当所有字段的宽度都是8个字节时, CanCompareBits错误地返回true,导致两个不同但语义相同的值按位比较。

当至少有一个字段不是8个字节宽时, CanCompareBits返回false,代码继续使用反射来遍历字段,并为每个值调用Equals ,正确地将-0.0等于0.0

这里是来自SSCLI的CanCompareBits的源代码:

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

我在http://blogs.msdn.com/xiangfan/archive/2008/09/01/magic-behind-valuetype-equals.aspx找到了答案。

核心部分是CanCompareBits的源代码评论, ValueType.Equals用它来确定是否使用memcmp -style比较:

CanCompareBits的评论说:“如果valuetype不包含指针并且紧密排列,则返回true”。 FastEqualsCheck使用“memcmp”来加速比较。

作者继续说明OP所描述的问题:

想象一下你有一个只包含浮点的结构。 如果一个包含+0.0,另一个包含-0.0,会发生什么? 它们应该是相同的,但潜在的二进制表示是不同的。 如果你嵌套其他结构覆盖Equals方法,那么优化也将失败。


Vilx的猜想是正确的。 “CanCompareBits”所做的是检查问题的值类型是否在内存中“紧密排列”。 通过简单比较构成结构的二进制位来比较紧密排列的结构; 通过在所有成员上调用Equals来比较松散包装的结构。

这解释了SLaks的观点,即它重复着所有双打的结构; 这样的结构总是紧密包装。

不幸的是,正如我们在这里所看到的那样,这引入了语义差异,因为双打的比较比较和双打的等同比较给出不同的结果。

链接地址: http://www.djcxy.com/p/66607.html

上一篇: Can anyone explain this strange behavior with signed floats in C#?

下一篇: Marshaling Struct Array with Strings Between c# and c++. Strings are empty