重写Object.Equals时,是否适合使用传入的对象的Equals(MyType)?

时间:2013-08-21 22:08:17

标签: c# iequatable

举一个简单的例子,假设你有两个在很多方面都不同的类,但仍然可以被认为是“等同的”:

class WholeNumber: IEquatable<WholeNumber> {
    int value;

    public override bool Equals(object obj) {
        if (obj is IEquatable<WholeNumber>) {
            IEquatable<WholeNumber> other = (IEquatable<WholeNumber>) obj;
            return other.Equals(this);
        } else {
            return false;
        }
    }

    public bool Equals(WholeNumber other) {
        return this.value == other.value;
    }
}

class Fraction : IEquatable<WholeNumber> {
    WholeNumber numerator;
    WholeNumber denominator;

    public bool Equals(WholeNumber other) {
        if (denominator != 1) {
            // Assume fraction is already reduced
            return false;
        } else {
            return this.numerator.Equals(other);
        }
    }
}

这将允许任何声称等于WholeNumber的对象被传递到WholeNumber的Equals(对象)函数,并获得所需的结果,而WholeNumber不需要知道任何其他类。

这种模式是个好主意吗?将IEquatable与其他类一起使用是一个常见的东西(它在哪里制造)?

1 个答案:

答案 0 :(得分:3)

不,这是一个坏主意。虽然此代码不会发生无限递归,但当您将Equals()个实例委托给其他人时,它仍然是一个持续的威胁。 (如果你使用这种方法,那么我强烈建议编写许多单元测试,以确保Equals()在所有情况下都能达到预期效果。)

请注意,当a.Equals((object)b)返回true时,a.GetHashCode() == b.GetHashCode()也必须为true。如果您无法确保new WholeNumber(2)new Fraction(2, 4)具有相同的哈希码,那么它们不应该按Equals(object) 进行比较。

我采用的做法是Equals(object)覆盖仅在参数的类型为或者从声明覆盖的类型派生时才返回true - 在本例中为obj is WholeNumber。如果这是真的,obj.GetType() != typeof(WholeNumber)则我会调用b.Equals(a),以便更多派生的Equals()获得优先权。

如果你需要与表兄弟类型相等,那么就是IEquatable<T>所在的地方。在这种情况下,你也会在IEquatable<Fraction>上实现WholeNumber,这对于委托是完全合理的到Fraction执行IEquatable<WholeNumber>.Equals()以避免重复该逻辑。 (提供从WholeNumberFraction的隐式转换在这里也可能是有益的。)