在.NET 4.0中,值类型的Equals的默认实现是什么?

Geb*_*ebb 27 .net c# clr

这两个文档页面似乎与此主题相矛盾:

它是按位平等还是反思?

我瞥了一眼源代码,ValueType发现评论说

//如果此对象中没有GC引用,我们可以避免反射

//并做一个快速的memcmp

有人可以澄清"GC参考"的含义吗?我想这是一个有引用类型的字段,但我不确定.

如果我创建一个struct只有值类型字段,那么它的实例是否总是与快速方式进行比较?

更新: .Net 4.5的文档已得到显着改进:它没有提到的矛盾,现在可以更好地理解默认值类型相等性检查的工作原理.

Chr*_*ens 43

System.ValueType.Equals很特别.它按顺序执行以下步骤,直到获得一些结果:

  1. 如果obj比较为'null',则返回false.
  2. 如果thisobj参数是不同的类型,则返回false.
  3. 如果类型是"blittable",它会比较内存图像.如果它们相同,则返回true.
  4. 最后,它使用反射来Equals为每个值调用配对的实例字段.如果这些字段中的任何一个不相等,则返回false.否则它会返回true.请注意,它从不调用基本方法Object.Equals.

因为它使用反射来比较字段,所以应始终覆盖 您创建的Equals任何字段ValueType.反思很慢.

当它是"GCReference"或结构中作为参考类型的字段时,它会使用每个字段上的反射来进行比较.它必须这样做,因为它struct实际上有一个指向引用类型在堆上的位置的指针.

如果结构中没有使用引用类型,并且它们是相同的类型,则保证字段的顺序相同,并且内存中的大小相同,因此它只能比较裸内存.

对于仅具有字段值类型的结构,即只包含一个int字段的结构,在比较期间不进行反射.没有字段引用堆上的任何内容,因此没有GCReferenceGCHandle.此外,此结构的任何实例都将具有相同的字段内存布局(有一些小的例外),因此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
}
Run Code Online (Sandbox Code Playgroud)

数字在数学上是相等的,但它们的二进制表示不相等.所以,我会再次强调它,不要依赖ValueType.Equals的默认实现

  • @Gebb - 你不必知道你在做什么来使用它.您可以依赖*Microsoft的*默认实现,但由于它不是标准的一部分,您可能会发现代码在Mono甚至.NET CE上的行为不同.此外,行为可能在未来发生变化,因为它无论如何都不是标准的.Microsoft建议您覆盖默认实现,这最终是最佳选择.如果完全可测量的话,无论你获得什么小的性能提升都可以忽略不计. (2认同)