两个文档页面似乎在这个主题上自相矛盾:
- ValueType.Equals Method说“Equals 方法的默认实现使用反射 来比较 obj 和此实例的相应字段。”
- Object.Equals Method (Object)说“Equals 的默认实现支持引用类型的引用相等,以及值类型的按位相等。”
那么是按位相等还是反射?
我瞥了一眼ValueType
的源代码,发现有评论说
// if there are no GC references in this object we can avoid reflection
// and do a fast memcmp
有人可以阐明“GC 引用”的含义吗?我猜这是一个具有引用类型的字段,但我不确定。
如果我创建一个只有值类型字段的 struct
,它的实例是否总是以快速方式进行比较?
更新:.Net 4.5 的文档得到了显着改进:它没有提到的矛盾,现在可以更好地理解默认值类型相等性检查的工作原理。
最佳答案
System.ValueType.Equals
很特别。它按顺序执行以下步骤,直到得到一些结果:
- 如果
obj
比较为“null”,则返回false
。 - 如果
this
和obj
参数是不同的类型,则返回false
。 - 如果类型是“blittable”,它会比较内存镜像。如果它们相同,则返回
true
。 - 最后,它使用反射调用
Equals
每个值的配对实例字段。如果这些字段中的任何一个不相等,则返回false
。否则返回true
。请注意,它从不调用基方法Object.Equals
。
因为它使用反射来比较字段,所以您应该始终覆盖 Equals
您创建的任何 ValueType
.反射很慢。
当它是“GCReference”或结构中的引用类型的字段时,它最终使用每个字段上的反射来进行比较。它必须这样做,因为 struct
实际上有一个指向引用类型在堆上的位置的指针。
如果struct中没有使用引用类型,并且它们是同一类型,则保证字段的顺序相同,并且在内存中的大小相同,因此它可以只比较裸内存。
对于字段只有值类型的结构,即只有一个 int
字段的结构,比较期间不会进行反射。没有字段引用堆上的任何内容,因此没有 GCReference
或 GCHandle
。此外,此结构的任何实例都将具有相同的字段内存布局(有一些小异常(exception)),因此 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.Equals 的默认实现
关于c# - 在 .NET 4.0 中,值类型的 Equals 默认实现是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8315656/