在.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已经得到了显著的改进:它没有了上面提到的矛盾,现在可以更好地理解默认值类型相等检查是如何工作的。

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

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
}

这两个数字在数学上是相等的,但在二进制表示中却不相等。因此,我将再次强调,不依赖于ValueType的默认实现。=

作为这个领域的真正专家,我只想继续说我的想法:文档(根据我的说法)指出,如果你的结构有一个字段是对象(引用类型),反射不能避免

所以如果你有以下内容:

    public struct SomeStruct
    {
        public object ObjectTest
    }

ObjectTest没有反射就不能比较。所以会用到反射。这部分文字似乎在说我是对的:

" ValueType。Equals - Equals方法的默认实现使用反射来比较obj和此实例的相应字段。"