我只是想知道为什么该语言的设计者决定在匿名类型上实现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连接和分组中的哈希键.出于这个原因,他们需要语义正确Equals和GetHashCode实现.
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)
因为它给了我们一些有用的东西.考虑以下:
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()逐字段相等的工作.
PersonInfoStore ,上面将更接近相同的查询,而不是linq-to-objects.(仍然不一样,它将匹配XML源将执行的操作,但不符合大多数数据库的排序将导致的结果).IEqualityComparer每个调用GroupBy- 为匿名对象定义IEqualityComparer是可能的但不容易 - 并且远非最自然的含义.第三点值得研究.
当我们定义一个值类型时,我们自然想要一个基于价值的平等概念.虽然我们可能对基于值的相等性的想法与默认值不同,例如匹配给定字段不区分大小写,但默认情况下自然是合理的(如果性能不佳和在一种情况下有错误*).(此外,在这种情况下,引用相等是没有意义的).
当我们定义引用类型时,我们可能想要也可能不想要基于值的相等概念.默认值为我们提供引用相等,但我们可以轻松地改变它.如果我们改变它,我们可以改变它只是Equals和GetHashCode或为他们也==.
当我们定义一个匿名类型时,哦等等,我们没有定义它,这就是匿名的意思!我们关心参考平等的大多数场景都不再存在.如果我们要将对象保持足够长的时间,以后想知道它是否与另一个对象相同,我们可能不会处理匿名对象.我们关心基于价值的平等的案例出现了很多.经常使用LINQ(GroupBy正如我们看到的上面,而且Distinct,Union,GroupJoin,Intersect,SequenceEqual,ToDictionary和ToLookup),并经常与其他用途(它不是像我们没有与可枚举做的事情的LINQ确实为我们在2.0和在一定程度上在此之前,任何用2.0编码的人都会写出一半的方法Enumerable.
总而言之,我们从平等与匿名类一起工作的方式中获益良多.
如果有人真的想要引用平等,那么==使用引用平等意味着他们仍然拥有引用平等,所以我们不会丢失任何东西.这是要走的路.
*默认的实现,Equals()并且GetHashCode()有一个优化,让它在安全的情况下使用二进制匹配.不幸的是,有一个错误使得它有时会错误地识别某些情况,因为这些情况对于这种更快的方法是安全的(如果不是这样的话,或者至少习惯了,可能它已经修复).一个常见的情况是,如果decimal在结构中有一个字段,那么它会将一些具有等效字段的实例视为不等.
| 归档时间: |
|
| 查看次数: |
12812 次 |
| 最近记录: |