簡體   English   中英

意外的相等測試,Equals(a, a) 評估為 false

[英]Unexpected equality tests, Equals(a, a) evaluates to false

給定結構 S1:

unsafe readonly struct S1
{
    public readonly int A1, A2;
    public readonly int* B;

    public S1(int a1, int a2, int* b)
    {
        A1 = a1;
        A2 = a2;
        B = b;
    }
}

和一個平等測試:

int x = 10;
var a = new S1(1, 2, &x);
var b = new S1(1, 2, &x);

var areEqual = Equals(a, b);  // true

正如預期的那樣, areEqual評估為 true。

現在讓我們稍微改變我們的結構為 S2(用字符串替換指針):

unsafe readonly struct S2
{
    public readonly int A1, A2;
    public readonly string C;

    public S2(int a1, int a2, string c)
    {
        A1 = a1;
        A2 = a2;
        C = c;
    }
}

使用模擬測試:

var a = new S2(1, 2, "ha");
var b = new S2(1, 2, "ha");

var areEqual = Equals(a, b);  // true

這也評估為真。

現在是有趣的部分。 如果我們將兩個結構組合到 S3:

unsafe readonly struct S3
{
    public readonly int A1, A2;
    public readonly int* B;
    public readonly string C;

    public S3(int a1, int a2, int* b, string c)
    {
        A1 = a1;
        A2 = a2;
        B = b;
        C = c;
    }
}

並測試是否相等:

int x = 10;
var a = new S3(1, 2, &x, "ha");
var b = new S3(1, 2, &x, "ha");

var areEqual = Equals(a, b);  // false

出乎意料地,相等測試失敗了。 甚至更糟,

Equals(a, a); // false

也沒有通過測試。

為什么最后兩個相等測試評估為假?

編輯

錯誤報告供參考。 已在 .net 6.0 中修復。

實例的實際比較由ValueType.Equals執行。 這是實現:

public override bool Equals(object? obj)
{
    if (null == obj)
    {
        return false;
    }
    Type thisType = this.GetType();
    Type thatType = obj.GetType();

    if (thatType != thisType)
    {
        return false;
    }

    object thisObj = (object)this;
    object? thisResult, thatResult;

    // if there are no GC references in this object we can avoid reflection
    // and do a fast memcmp
    if (CanCompareBits(this))
        return FastEqualsCheck(thisObj, obj);

    FieldInfo[] thisFields = thisType.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

    for (int i = 0; i < thisFields.Length; i++)
    {
        thisResult = thisFields[i].GetValue(thisObj);
        thatResult = thisFields[i].GetValue(obj);

        if (thisResult == null)
        {
            if (thatResult != null)
                return false;
        }
        else
        if (!thisResult.Equals(thatResult))
        {
            return false;
        }
    }

    return true;
}

我們可以看到,如果沒有 GC 引用(在您的字段中引用的類),它基本上會執行位級比較,否則它將在您的結構的每個字段上調用 ​​Equals。

當您有一個指針類型的字段(在您的情況下為 int*)並且您使用 Reflection 獲取其值時,您獲得的值將作為System.Reflection.Pointer裝箱。

我們可以看到它是一個類,因此不會進行位級比較。

所以它會調用Pointer.Equals,但不幸的是我們可以通過Pointer類源代碼看到它沒有被覆蓋,所以執行的檢查將是對象的引用是否相同:

public sealed unsafe class Pointer : ISerializable
{
    // CoreCLR: Do not add or remove fields without updating the ReflectionPointer class in runtimehandles.h
    private readonly void* _ptr;
    private readonly Type _ptrType;

    private Pointer(void* ptr, Type ptrType)
    {
        Debug.Assert(ptrType.IsRuntimeImplemented()); // CoreCLR: For CoreRT's sake, _ptrType has to be declared as "Type", but in fact, it is always a RuntimeType. Code on CoreCLR expects this.
        _ptr = ptr;
        _ptrType = ptrType;
    }

    public static object Box(void* ptr, Type type)
    {
        if (type == null)
            throw new ArgumentNullException(nameof(type));
        if (!type.IsPointer)
            throw new ArgumentException(SR.Arg_MustBePointer, nameof(ptr));
        if (!type.IsRuntimeImplemented())
            throw new ArgumentException(SR.Arg_MustBeType, nameof(ptr));

        return new Pointer(ptr, type);
    }

    public static void* Unbox(object ptr)
    {
        if (!(ptr is Pointer))
            throw new ArgumentException(SR.Arg_MustBePointer, nameof(ptr));
        return ((Pointer)ptr)._ptr;
    }

    void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
    {
        throw new PlatformNotSupportedException();
    }

    internal Type GetPointerType() => _ptrType;
    internal IntPtr GetPointerValue() => (IntPtr)_ptr;
}

因此比較將返回 false,因為您有一個指針。

暫無
暫無

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

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