使用 IEquatable的同级类的 C# 字典唯一性
我想将两个类的实例存储在字典结构中,并使用 IEquatable 来确定这些实例的唯一性。这两个类共享一个(抽象)基类。考虑以下类:
abstract class Foo
{
...
}
class SubFoo1 : Foo
{
...
}
class SubFoo2 : Foo
{
...
}
字典将被声明: Dictionary
哪些类应声明为 IEquatable?这些声明的泛型类型 T 应该是什么?这可能吗?
I would like to store insances of two classes in a dictionary structure and use IEquatable to determine uniqueness of these instances. Both of these classes share an (abstract) base class. Consider the following classes:
abstract class Foo
{
...
}
class SubFoo1 : Foo
{
...
}
class SubFoo2 : Foo
{
...
}
The dictionary will be delcared: Dictionary<Foo, Bar>
Which classes should be declared as IEquatable? And what should the generic type T be for those declarations? Is this even possible?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
不要将任何内容声明为
IEquatable
。正如马克所说,当涉及到继承和平等时,这通常是一个问题。相反,计算出您想要此特定字典中的键的相等性,并实现 IEqualityComparer,然后将其传递到字典构造函数中。
通常如何比较特定情况下的键是相当清楚的。
Don't declare anything as
IEquatable<T>
. It's generally a problem when it comes to inheritance and equality, as Marc says.Instead, work out what kind of equality you want for the keys in this particular dictionary, and implement
IEqualityComparer<Foo>
, then pass that into the dictionary constructor.Usually it's reasonably clear how to compare keys for a specific situation.
这取决于你的课程代表什么。因此,没有“通用”解决方案。如果存在,那么
System.Object
将实现IEquatable
长话短说,这取决于您想要通过类层次结构实现什么目标。
长话短说,我同意无论如何都很难确定是什么使对象相等,因为即使对于相同的对象类型,也可能有不同的要求。当您使用缓存时,相等性将由对象的实体 ID 确定。当您使用执行/撤消列表时,您需要另一个限定符,因为列表中每个对象的 ID 可能都是相同的。
我想我有点离题了,但我希望我能给你一些有用的观点。 ;-)
It depends on what you classes stand for. So there is no 'generic' solution to that. If there was, then
System.Object
would implementIEquatable<object>
, as everything inherits fromobject
already anyway. So you could have asked the same question aboutObject
instead ofFoo
.Long story short, it depends on what you want to achieve with your class hierarchy.
Short story long, I agree it is difficult to determine what makes objects equal anyway, because even for the same object type, there might be different requirements. When you use a Cache, equality would be determined by an Entity ID of the object. When you use a Do-/Undo-List, you'd need another qualifier because the ID will probably be the same for each object in the list.
Guess I digressed a bit, but I hope I could give you some helpful points. ;-)
您始终可以在字典构造函数中指定自己的相等比较器。如果您不这样做,字典将查找
IEquatable
,因此在您的情况下为IEquatable
因此您应该实现该实现。You can always specify your own equality comparer in the dictionary constructor. If you don't, the dictionary will look for
IEquatable<TKey>
so in your case forIEquatable<Foo>
therefore you should implement that one.当您开始获得继承时,选择
T
确实变得很棘手。基本类型是显而易见的选择,但我想知道仅重写Equals
和GetHashCode
是否更合适。您还可能得出结论,这种复杂的场景可能不是密钥的糟糕选择,因此应使用更简单、更可预测的密钥。特别是,您希望“散列”基于类型的某些不可变方面,并且“散列”和“等于”必须兼容。如果类型没有
密封
,这些细微差别很难保证。When you start getting inheritance, it indeed gets tricky to choose a
T
. The base-type is the obvious choice, but I wonder if it isn't more appropriate to just overrideEquals
andGetHashCode
.You might also conclude that such complex scenarios might be poor choices for a key, and use a simpler, more predictable key instead. In particular you want the "hash" to be based on some immutable facet of the type, and the "hash" and "equals" must be compatible. These nuances are very hard to guarantee if the type isn't
sealed
.可继承类几乎不应该实现的对象都应该这样做,以便 .Equals,后者的语义与前者匹配的唯一方法是接口实现简单地调用虚拟
IEquatable
。对于任何类型T
,每个实现 IEquatableIEquatable.Equals
的语义匹配是Object.Equals
的。如果一个类为某些Foo
实现了IEquatable
,并且派生类更改了Object.Equals
的语义,但不重新实现 < code>IEquatableObject.Equals
本身;如果接口只是将其参数传递给Object.Equals
,那么实现它没有任何好处。Inheritable classes should almost never implement
IEquatable<T>
. For any typeT
, every object which implementsIEquatable<T>
should do so such that the semantics ofIEquatable<T>.Equals
will match that ofObject.Equals
. If a class implementsIEquatable<Foo>
for someFoo
and a derived class changes the semantics ofObject.Equals
but does not re-implementIEquatable<Foo>.Equals
, the only way the semantics of the latter will match the former will be if the interface implementation simply calls the virtualObject.Equals
itself; if the interface is simply going to pass its argument toObject.Equals
, there is no benefit to implementing it.