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

时间:2011-11-29 18:18:24

标签: c# .net clr

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

它是按位相等还是反思?

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

  

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

     

//并做一个快速的memcmp

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

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

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

2 个答案:

答案 0 :(得分:43)

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

  1. 如果obj比较为'null',则会返回false
  2. 如果thisobj参数的类型不同,则返回false
  3. 如果类型为“blittable”,则会比较内存图像。如果它们相同,则返回true
  4. 最后,它使用反射为每个值调用Equals配对的实例字段。如果这些字段中的任何一个不相等,则返回false。否则返回true。请注意,它从不调用基本方法Object.Equals
  5. 由于它使用反射来比较字段,因此您应该在您创建的任何Equals 始终覆盖 ValueType。反思很慢。

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

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

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