gpt4 book ai didi

c# - 意外的相等测试,Equals(a, a) 评估为 false

转载 作者:行者123 更新时间:2023-12-03 17:15:29 30 4
gpt4 key购买 nike

给定结构 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
也没有通过测试。
为什么最后两个相等测试评估为假?
编辑
Bugreport以供引用。已在 .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* )并且你使用反射来获取它的值时,你得到的值被装箱为 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,因为您有一个指针。

关于c# - 意外的相等测试,Equals(a, a) 评估为 false,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63956533/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com