【问题标题】:Correct way to override Equals(object obj) when dealing with sub/superclasses?处理子/超类时覆盖 Equals(object obj) 的正确方法?
【发布时间】:2012-10-11 12:06:42
【问题描述】:
我正在使用以下代码来测试 Equals
public override bool Equals(object obj)
{
// Equals must return false on compares to null.
if (obj == null || GetType() != obj.GetType())
return false;
Foo fooItem = obj as Foo;
return fooItem.FooId == this.FooId;
}
如果 obj 是一个超类,我应该如何处理它?它应该总是返回假,还是有时它应该是真的?
如何进行比较?
【问题讨论】:
标签:
c#
.net
superclass
gethashcode
iequalitycomparer
【解决方案1】:
这真的取决于你想要平等的含义。
我认为我从来没有允许不同班级之间的平等。不过,您可能有一个案例。
请记住,您可以使用自定义 IEqualityComparer<T>。每当您想以一种并非真正通用的方式测试相等性时,这是一个很好的策略,因为这意味着其他情况下的其他代码不会被您的覆盖所困扰。每种类型只能覆盖一次 Equals,但可以拥有任意数量的比较器。
【解决方案2】:
只需摆脱 GetType() 比较并将 obj as Foo 移动到第一行。
代码将是
public override bool Equals(object obj)
{
var fooItem = obj as Foo;
// Equals must return false on compares to null.
if (obj == null) {
return false;
}
return fooItem.FooId == this.FooId;
}
这假设所有 Foo 甚至派生一次都有意义的全值比较,或者实现上述 Equals 覆盖的类是密封的。
如果对所有 Foo(包括派生一次)没有有意义的值比较,那么我建议改用 IEqualityComaprer。与所有其他实例方法一样,只有当它们确实是类型的一部分而不仅仅是辅助方法时,您才应该将它们包含在类层次结构中
【解决方案3】:
虽然在某些情况下,让不同类型的对象报告自己彼此相等可能很有用,但我强烈建议不要这样使用;我唯一能认为这是合理的情况是,如果所有类型都继承自一个公共基类,其中包括使用各种虚拟方法的密封Equals 方法,询问对象它们支持的加速相等比较的方法,使用最佳可用方法,如果没有可用的加速方法,则将对象转换为规范形式并进行比较。
例如,考虑抽象的ImmutableFloatingPointMatrix 类型,它实现了一个双参数索引属性获取器。如果两个矩阵具有相同的大小并且在相同的位置具有相同的值,则它们应该被认为是相等的。虽然派生类可以使用二维数组来保存矩阵的内容,但也可以使用许多其他存储方法。例如,可能有一个具体的IdentityMatrix 类,它有一个Size 字段,总是将它的维度报告为SizexSize,并且有一个索引属性,它为主对角线上的单元格返回 1.0 和 0.0别处。也可以有一个DiagonalMatrix,它使用一个Size-item 数组来保存对角线上的东西。虽然可以通过检查所有相应的元素来比较任何两个矩阵的相等性,例如检查DiagonalMatrix 与 IdentitymMatrix 可以通过确保两个矩阵大小相同并且数组中的所有项目都等于 1.0 来更好地处理(不必费心检查主对角线之外的任何点,因为不会有任意)。
即使对于 Equals 的语义可以合理定义的类型,以便不同类型的对象将自己报告为相等,这也可能不是最好的主意。在可以使用自定义EqualityComparer 的情况下,这可能比Equals 的奇怪覆盖更好。