为什么要对引用类型使用值相等 [重复]

Why Use Value Equality On a Reference Type [duplicate]

提问人:Rabadash8820 提问时间:9/25/2016 更新时间:9/25/2016 访问量:655

问:

这篇 MSDN 文章阐明了何时将类型声明为值类型 (struct) 与引用类型 (class)。特别是,它说:

除非该类型具有以下所有特征,否则请避免定义结构:

  • 它在逻辑上表示单个值,类似于原始类型(int、double 等)。
  • 它的实例大小小于 16 字节。
  • 它是不可变的。
  • 它不必经常装箱。

在所有其他情况下,应将类型定义为类。

然后,这篇 MSDN 文章阐明了在类型上重写 Object.Equals() 的一些准则,包括:

当一个类型是不可变的时,...重载运算符 == 来比较值相等而不是引用相等可能很有用,因为作为不可变对象,只要它们具有相同的值,就可以认为它们是相同的。在非不可变类型中覆盖运算符 == 不是一个好主意。

所以在我看来,如果一个类型是不可变的,你只需将其声明为结构并覆盖 ValueType.Equals()。但是,如果你的类型被声明为类,那么理论上你只需要引用语义这篇 SO 帖子似乎支持这一点。所以我的问题是,你为什么要费心在引用类型上覆盖 Equals()?这样做就像说引用语义对于你的引用类型来说不够好,这对我来说毫无意义。我能想到的唯一例子是,如果你有一个大型(>16 字节实例)类,它混合了可变和不可变字段,但即便如此,你似乎只需要引用语义。

C# .NET 结构 相等

评论

0赞 Ivan Stoev 9/25/2016
上课怎么样?string
0赞 Rabadash8820 9/25/2016
哇,这是一个很好的例子,我觉得自己像个白痴......你能给我举个例子吗?如果是这样,我可能会把这个问题记下来。
1赞 Rabadash8820 9/25/2016
元组...也是一个不错的选择。那么,您怎么看,在引用的 SO 帖子和我刚刚找到的另一篇帖子之间(stackoverflow.com/questions/104158/......
1赞 Ivan Stoev 9/25/2016
顺便说一句,埃里克·利珀特 (Eric Lippert) 有一些关于平等的帖子,您可能会觉得很有趣 - C#:不一致的平等和尖锐的遗憾:十大最糟糕的 C# 功能 #9:太多的平等
1赞 Rabadash8820 9/25/2016
太棒了,谢谢你的链接。我真的很感谢你的指导!

答:

1赞 Emperor Eto 9/25/2016 #1

对于您的具体问题 - “您为什么要在引用类型上重写 Equals()?”:如果您希望其他 .NET 方法将类的两个实例视为相等,即使它们不是同一个实例,也可以重写 Equals()。

考虑。通过重写,可以确保该方法提供正确的结果,即使被比较的特定类实例实际上不是同一个实例。List<T>.Contains()Equals()

我自己在WPF中经常遇到这种情况。我遇到过这样一种情况,即某些控件(特别是)具有我的对象的副本,这些副本与我测试它们的实例不同。这使得像 和属性这样的方法无法正常工作,直到我覆盖了该类。我能想到一个肯定会发生这种情况的特定示例:假设您有一个类,其中包含作为数据库查询结果创建的实例列表。用该结果集填充 。稍后,您从另一个数据库查询(也许是某种筛选器)中获得了一个新结果集,现在想要选择第二个查询结果中的所有项目。如果不覆盖 ,则无法设置 ,因为 中的实例将与第二个查询中的实例不同。正如评论中指出的那样,还有其他方法可以解决这个问题,但在我看来,覆盖提供了最干净的解决方案。ListBoxScrollIntoViewSelectedItemEqualsPersonListBoxListBoxEqualsSelectedItemsListBoxEquals

这只是一个例子。我相信你很快就会遇到其他人。

评论

0赞 15ee8f99-57ff-4f92-890c-b56153 9/25/2016
“许多控件(如 ListBox 的)实际上会创建浅拷贝”——这不是我的经验;你能提供代码来重现这种行为吗?
0赞 15ee8f99-57ff-4f92-890c-b56153 9/25/2016
对于你的第二段,服务得很好。coll.Where(x => x.ID == someItemID)
0赞 Emperor Eto 9/25/2016
Ed - 既然你提到了它,我很难重现我以前遇到的问题,但有一种情况是我需要重写 Equals 才能使 ListBox.ScrollIntoView、SelectedItem 和相关成员正常工作。我记得有人向我解释为什么会出现一种正在制作浅层副本的情况。我将编辑我的答案以澄清。
0赞 15ee8f99-57ff-4f92-890c-b56153 9/25/2016
我遇到了同样的问题,但这不是创建副本的 ListBox,而是反序列化。
0赞 Emperor Eto 9/25/2016
是的,那肯定会做到的。不同的数据库查询也是如此。或者从磁盘重新读取项目。它可能发生的很多场景。