在.net 4.0中,值类型的Equals的默认实现是什么?
本文关键字:Equals 默认 实现 是什么 类型 net | 更新日期: 2023-09-27 18:16:12
这两个文档页似乎在这个主题上有矛盾:
- ValueType。Equals方法说:"Equals方法的默认实现使用反射来比较obj和此实例的相应字段。"
- 对象。Equals方法(对象)说:"Equals的默认实现支持引用类型的引用相等,以及值类型的位相等。"
那么它是位相等还是反射?
我瞥了一眼ValueType
的源代码,发现一个注释说
//如果这个对象中没有GC引用,我们可以避免反射
//执行快速memcmp
谁能解释一下"GC引用"是什么意思?我猜这是一个有引用类型的字段,但我不确定。
如果我创建一个只有值类型字段的struct
,它的实例是否总是以快速的方式进行比较?
UPDATE:。Net 4.5已经得到了显著的改进:它没有了上面提到的矛盾,现在可以更好地理解默认值类型相等检查是如何工作的。
System.ValueType.Equals
是特别的。它按顺序执行以下步骤,直到得到某个结果:
- 如果
obj
比较为'null',则返回false
。 - 如果
this
和obj
参数是不同的类型,它返回false
。 - 如果类型是"blittable",它比较内存图像。如果它们相同,则返回
true
。 - 最后,它使用反射为每个值调用
Equals
成对的实例字段。如果这些字段中的任何一个不相等,则返回false
。否则返回true
。注意,它从不调用基方法Object.Equals
。
因为它使用反射来比较字段,你应该总是覆盖 Equals
在你创建的任何ValueType
。
当它是一个"GCReference",或者结构体中的字段是引用类型时,它会在每个字段上使用反射来进行比较。它必须这样做,因为struct
实际上有一个指向引用类型在堆上的位置的指针。
如果在结构体中没有使用引用类型,并且它们是相同的类型,则保证字段的顺序相同,并且在内存中具有相同的大小,因此它可以只比较裸内存。
对于字段只有值类型的结构体,即只有一个int
字段的结构体,在比较期间不进行反射。这些字段都没有引用堆上的任何内容,因此没有GCReference
或GCHandle
。此外,这种结构的任何实例都将具有相同的字段在内存中的布局(除了一些小的例外),因此CLR团队可以进行直接内存比较(memcmp),这比其他选项快得多。
所以,是的,如果你的结构中只有值类型,它会做更快的memcmp,而不是反射比较,但你可能不想这样做。继续阅读。
这个并不意味着您应该使用默认的Equals
实现。事实上,不要那样做。阻止它。它在做位比较,不是总是准确的。你在说什么?我给你示范一下:
private struct MyThing
{
public float MyFloat;
}
private static void Main(string[] args)
{
MyThing f, s;
f.MyFloat = 0.0f;
s.MyFloat = -0.0f;
Console.WriteLine(f.Equals(s)); // prints False
Console.WriteLine(0.0f == -0.0f); // prints True
}
这两个数字在数学上是相等的,但在二进制表示中却不相等。因此,我将再次强调,不依赖于ValueType的默认实现。=
作为这个领域的真正专家,我只想继续说我的想法:文档(根据我的说法)指出,如果你的结构有一个字段是对象(引用类型),反射不能避免。
所以如果你有以下内容:
public struct SomeStruct
{
public object ObjectTest
}
ObjectTest没有反射就不能比较。所以会用到反射。这部分文字似乎在说我是对的:
" ValueType。Equals - Equals方法的默认实现使用反射来比较obj和此实例的相应字段。"