为什么匿名types等于实现比较字段?
就像问题一样,我只是想知道为什么语言的devise者们希望在匿名types上实现Equals,就像价值types一样。 这不是误导吗?
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 }
乍一看,所有打印的布尔值应该是false。 但使用Persontypes时,带有Equals调用的行会返回不同的值,并使用匿名types。
匿名types实例是不可变的数据值,没有行为或身份。 引用比较它们没有什么意义。 在这方面,我认为为他们进行结构上的平等比较是完全合理的。
如果要将比较行为切换为自定义(引用比较或不区分大小写),可以使用Resharper将匿名types转换为指定的类。 Resharper也可以生成平等成员。
这样做也有一个非常实际的理由:匿名types可以方便地在LINQ连接和分组中使用散列键。 出于这个原因,他们需要语义上正确的Equals
和GetHashCode
实现。
为什么部分你应该问语言devise师…
但是,我在Eric Lippert的关于匿名types统一的文章中发现了这一点,第二部分
匿名types为您提供了一个方便的地方来存储一个小的不可变的名称/值对,但它给了你更多的东西。 它也给你一个Equals,GetHashCode的实现,和这个讨论最相关的ToString。 (*)
为什么部分出现在注释中:
(*)我们为您提供Equals和GetHashCode,以便您可以在LINQ查询中使用匿名types的实例作为执行连接的键。 由于性能原因,LINQ to Objects使用哈希表实现连接,因此我们需要Equals和GetHashCode的正确实现。
C#语言规范(可在此获得)的官方答复:
匿名types的Equals和GetHashcode方法会覆盖从对象inheritance的方法,并且会根据属性的Equals和GetHashcode进行定义,以便相同匿名types的两个实例在所有属性相等时均相等 。
(我的重点)
其他答案解释了为什么这样做。
因为它给了我们一些有用的东西。 考虑以下几点:
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()};
这些工作是因为匿名types的Equals()
和GetHashCode()
实现是在逐场平等的基础上工作的。
- 这意味着上述将更接近相同的查询时运行在不是linq对象的
PersonInfoStore
。 (仍然不一样,它将匹配XML源代码,但不是大多数数据库的sorting结果)。 - 这意味着我们不必为每个
GroupBy
调用定义一个IEqualityComparer
,这会使匿名对象变得非常困难 – 为匿名对象定义IEqualityComparer可能但并不容易,而且远不是最自然的意思。 - 首先,大多数情况下不会造成问题。
第三点值得研究。
当我们定义一个值types时,我们自然需要一个基于价值的平等概念。 虽然我们可能对基于价值的平等有一个不同的想法,比如默认情况下匹配一个给定的字段,但默认情况下是合理的(如果在一种情况下效果差和错误*)。 (另外,在这种情况下,引用相等是没有意义的)。
当我们定义一个引用types时,我们可能会也可能不需要一个基于价值的平等概念。 默认给了我们参考平等,但我们可以很容易地改变这一点。 如果我们改变它,我们可以改变它只是Equals
和GetHashCode
或为他们也==
。
当我们定义一个匿名types,等等,我们没有定义它,这就是匿名的意思! 我们关心引用平等的大多数情况都不存在了。 如果我们将持有一个对象足够长的时间,以后想知道它是否与另一个相同,我们可能不处理一个匿名对象。 我们关心基于价值的平等的情况出现了很多。 很多时候Linq( GroupBy
就像我们上面看到的那样,还有Distinct
, Union
, ToDictionary
, Intersect
, SequenceEqual
, ToDictionary
和ToLookup
)并经常和其他用途(这不是我们没有做Linq为我们用枚举数2.0,在某种程度上,在2.0之前编码的任何人都会在Enumerable
写入一半的方法)。
总的来说,我们从平等与匿名课程的方式中获得了很多。
如果有人真的希望引用平等,那么==
使用引用相等意味着他们仍然有这个,所以我们不会失去任何东西。 这是要走的路。
* Equals()
和GetHashCode()
的默认实现有一个优化,让它在安全的情况下使用二进制匹配。 不幸的是,有一个错误使得它有时会错误地将某些情况误认为是安全的,因为这种情况不是(或者至less是以前,也许是固定的)。 一个常见的情况是,如果你在结构中有一个decimal
字段,那么它会考虑一些具有相等字段的实例。