这两个文档页面似乎与此主题相矛盾:
它是按位相等还是反思?
我瞥了一眼ValueType
的源代码,发现了一条评论说
//如果此对象中没有GC引用,我们可以避免反射
//并做一个快速的memcmp
有人可以澄清“GC参考”的含义吗?我猜这是一个有引用类型的字段,但我不确定。
如果我创建一个只有值类型字段的struct
,那么它的实例是否总是与快速方式进行比较?
更新:.Net 4.5的文档已得到显着改进:它没有上述矛盾,现在可以更好地理解默认值类型相等检查的工作原理。
答案 0 :(得分:43)
System.ValueType.Equals
很特别。它按顺序执行以下步骤,直到得到一些结果:
obj
比较为'null',则会返回false
。this
和obj
参数的类型不同,则返回false
。true
。 Equals
配对的实例字段。如果这些字段中的任何一个不相等,则返回false
。否则返回true
。请注意,它从不调用基本方法Object.Equals
。由于它使用反射来比较字段,因此您应该在您创建的任何Equals
上 始终覆盖 ValueType
。反思很慢。
当它是“GCReference”或结构中作为参考类型的字段时,它会在每个字段上使用反射进行比较。它必须这样做,因为struct
实际上有一个指向引用类型在堆上的位置的指针。
如果结构中没有使用引用类型,并且它们是相同的类型,则保证字段的顺序相同,并且内存中的大小相同,因此它只能比较裸内存。 / p>
对于仅包含字段值类型的结构,即只包含一个int
字段的结构,在比较期间不会进行反射。没有字段引用堆上的任何内容,因此没有GCReference
或GCHandle
。此外,此结构的任何实例都将具有相同的字段内存布局(有一些小的例外),因此CLR团队可以进行直接内存比较(memcmp),这比其他选项快得多。 / p>
所以,是的,如果您的结构中只有值类型,它将执行更快的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的默认实现
答案 1 :(得分:0)
不是这个领域的真正专家我会继续并提出我的想法: 文档(根据我的说法)指出,如果你的结构有一个对象(引用类型)的字段,则反射不能。
所以如果你有以下内容:
public struct SomeStruct
{
public object ObjectTest
}
如果没有反射,无法比较ObjectTest。因此将使用反射。这部分内容似乎说我是对的:
“ ValueType.Equals - Equals方法的默认实现使用反射来比较obj和此实例的相应字段。”