为什么匿名类型的Equals实现比较字段?

dra*_*fly 55 c# c#-3.0

我只是想知道为什么该语言的设计者决定在匿名类型上实现Equals,类似于Equals值类型.这不是误导吗?

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

public static void ProofThatAnonymousTypesEqualsComparesBackingFields()
{
    var personOne = new { Name = "Pawe?", Age = 18 };
    var personTwo = new { Name = "Pawe?", Age = 18 };

    Console.WriteLine(personOne == personTwo); // false
    Console.WriteLine(personOne.Equals(personTwo)); // true
    Console.WriteLine(Object.ReferenceEquals(personOne, personTwo)); // false

    var personaOne = new Person { Name = "Pawe?", Age = 11 };
    var personaTwo = new Person { Name = "Pawe?", Age = 11 };
    Console.WriteLine(personaOne == personaTwo); // false
    Console.WriteLine(personaOne.Equals(personaTwo)); // false
    Console.WriteLine(Object.ReferenceEquals(personaOne, personaTwo)); // false
}
Run Code Online (Sandbox Code Playgroud)

乍一看,所有打印的布尔值都应该为false.但是Equals当使用Person类型时,带有调用的行返回不同的值,并使用匿名类型.

usr*_*usr 62

匿名类型实例是不具有行为或标识的不可变数据值.参考比较它们没有多大意义.在这种情况下,我认为为它们产生结构性平等比较是完全合理的.

如果要将比较行为切换为自定义(引用比较或不区分大小写),可以使用Resharper将匿名类型转换为命名类.Resharper还可以生成平等成员.

这样做还有一个非常实际的原因:匿名类型可以方便地用作LINQ连接和分组中的哈希键.出于这个原因,他们需要语义正确EqualsGetHashCode实现.

  • 因为==不会在C#中调用Equals(从不!).它调用`operator ==`,匿名类型没有那个.所以C#使用引用相等.是好还是坏?谁知道,因为这是一种权衡.毕竟,我从未觉得需要比较两个匿名类型实例.从来没有因某种原因发生过 (10认同)
  • 让operator ==返回与Equals不同的东西只是在寻找麻烦. (5认同)

nem*_*esv 36

为什么你应该问语言设计师...

但我在Eric Lippert的文章中发现了这一点,这篇文章是关于Anmbly Types Unify Within a Assembly,第二部分

匿名类型为您提供了一个存储一组不可变的名称/值对的便利位置,但它为您提供了更多.它还为您提供了Equals,GetHashCode的实现,以及与此讨论最密切相关的ToString.(*)

该部分的原因在于:

(*)我们为您提供Equals和GetHashCode,以便您可以在LINQ查询中使用匿名类型的实例作为执行连接的键.由于性能原因,LINQ to Objects使用哈希表实现连接,因此我们需要正确实现Equals和GetHashCode.


Ger*_*old 17

C#语言规范的官方答案(可在此处获得):

匿名类型上的Equals和GetHashcode方法重写从object继承的方法,并根据属性的Equals和GetHashcode定义,因此当且仅当所有属性相等时,同一匿名类型的两个实例才相等.

(我的重点)

其他答案解释了为什么这样做.

值得注意的是,在VB.Net中,实现方式有所不同:

没有键属性的匿名类型的实例仅等于它自己.

创建匿名类型对象时,必须明确指示键属性.默认值为:无密钥,这对C#用户来说可能非常混乱!

这些对象在VB中不相同,但是在C#等效代码中:

Dim prod1 = New With {.Name = "paperclips", .Price = 1.29}
Dim prod2 = New With {.Name = "paperclips", .Price = 1.29}
Run Code Online (Sandbox Code Playgroud)

这些对象评估为"相等":

Dim prod3 = New With {Key .Name = "paperclips", .Price = 1.29}
Dim prod4 = New With {Key .Name = "paperclips", .Price = 2.00}
Run Code Online (Sandbox Code Playgroud)


Jon*_*nna 9

因为它给了我们一些有用的东西.考虑以下:

var countSameName = from p in PersonInfoStore
  group p.Id by new {p.FirstName, p.SecondName} into grp
  select new{grp.Key.FirstName, grp.Key.SecondName, grp.Count()};
Run Code Online (Sandbox Code Playgroud)

这些工作是因为匿名类型的实现Equals()GetHashCode()逐字段相等的工作.

  1. 这意味着当运行时PersonInfoStore ,上面将更接近相同的查询,而不是linq-to-objects.(仍然不一样,它将匹配XML源将执行的操作,但不符合大多数数据库的排序将导致的结果).
  2. 这意味着我们不必为匿名对象定义一个非常难以对其进行分组的IEqualityComparer每个调用GroupBy- 为匿名对象定义IEqualityComparer是可能的但不容易 - 并且远非最自然的含义.
  3. 最重要的是,它不会导致大多数情况下的问题.

第三点值得研究.

当我们定义一个值类型时,我们自然想要一个基于价值的平等概念.虽然我们可能对基于值的相等性的想法与默认值不同,例如匹配给定字段不区分大小写,但默认情况下自然是合理的(如果性能不佳和在一种情况下有错误*).(此外,在这种情况下,引用相等是没有意义的).

当我们定义引用类型时,我们可能想要也可能不想要基于值的相等概念.默认值为我们提供引用相等,但我们可以轻松地改变它.如果我们改变它,我们可以改变它只是EqualsGetHashCode或为他们也==.

当我们定义一个匿名类型时,哦等等,我们没有定义它,这就是匿名的意思!我们关心参考平等的大多数场景都不再存在.如果我们要将对象保持足够长的时间,以后想知道它是否与另一个对象相同,我们可能不会处理匿名对象.我们关心基于价值的平等的案例出现了很多.经常使用LINQ(GroupBy正如我们看到的上面,而且Distinct,Union,GroupJoin,Intersect,SequenceEqual,ToDictionaryToLookup),并经常与其他用途(它不是像我们没有与可枚举做的事情的LINQ确实为我们在2.0和在一定程度上在此之前,任何用2.0编码的人都会写出一半的方法Enumerable.

总而言之,我们从平等与匿名类一起工作的方式中获益良多.

如果有人真的想要引用平等,那么==使用引用平等意味着他们仍然拥有引用平等,所以我们不会丢失任何东西.这是要走的路.

*默认的实现,Equals()并且GetHashCode()有一个优化,让它在安全的情况下使用二进制匹配.不幸的是,有一个错误使得它有时会错误地识别某些情况,因为这些情况对于这种更快的方法是安全的(如果不是这样的话,或者至少习惯了,可能它已经修复).一个常见的情况是,如果decimal在结构中有一个字段,那么它会将一些具有等效字段的实例视为不等.

  • @PeterRitchie 这不是我刚才说的吗? (2认同)