在C#接口实现中覆盖equals

时间:2014-02-28 17:17:30

标签: c# equals interface-implementation

我有一个实现接口的类,例如:

interface IInterface
{
   string PropA { get; }
   string PropB { get; }
}

class AClass : IInterface
{
   string PropA { get; protected set; }
   string PropB { get; protected set; }
}

平等基于PropA和PropB确定。覆盖AClass的Equals方法时,我是否应该尝试将obj强制转换为AClass,如下所示:

public override bool Equals(object obj)
{
   AClass other = obj as AClass;
   return other != null 
       && AClass.PropA == other.PropA 
       && AClass.PropB == PropB;
}

或者我应该尝试将obj强制转换为IInterface,如下所示:

public override bool Equals(object obj)
{
   IInterface other = obj as IInterface;
   return other != null 
       && AClass.PropA == other.PropA 
       && AClass.PropB == PropB;
}

3 个答案:

答案 0 :(得分:6)

可以做任何你想做的事。这两者在功能上是不一样的,但对你来说这是“正确的”是我们无法回答的。如果我有一个实现相同接口的BClass类,并且它对两个属性都有相同的值,那么 它应该等于你的AClass对象?如果是的话,做后者,如果没有,做前者。

就个人而言,我会发现后者有关。一般来说,我发现如果一个类要实现自己的个人等式定义,其他类不应该等于它。一个主要原因是,如果平等是对称的,那是更可取的。也就是说aclass.Equals(bclass)应该返回与bclass.Equals(aclass)相同的内容。当您不将相等性限制为相同类型时获得该行为是很难的。它需要所有相关课程的合作。

如果你有一些令人信服的理由来比较IInterface实现它们可能是不同的底层类但仍然是“相等”的,我个人更喜欢创建一个定义相等的IEqualityComparer<IInterface> 用于该界面。这将与两个实现类中任何一个的相等定义分开。

答案 1 :(得分:2)

决定你需要它如何运作。

Resharper实施:

class AClass : IInterface, IEquatable<AClass>
{
    public bool Equals(AClass other)
    {
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;
        return string.Equals(this.PropA, other.PropA) && string.Equals(this.PropB, other.PropB);
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (obj.GetType() != typeof (AClass)) return false;
        return Equals((AClass)obj);
    }

    public override int GetHashCode()
    {
        unchecked
        {
            return ((this.PropA != null ? this.PropA.GetHashCode() : 0) * 397) ^ (this.PropB != null ? this.PropB.GetHashCode() : 0);
        }
    }

    public string PropA { get; protected set; }
    public string PropB { get; protected set; }
}

答案 2 :(得分:0)

如果接口的目的是向消费者隐瞒两个等效对象可能属于不同类的事实,那么定义一个包含该接口类型的单个私有字段并链接到该接口的结构可能是个好主意。适当的接口方法。使用这样的结构通常应该基本上与使用接口类型的变量一样有效(主要的例外是如果结构最终被装箱),但是会阻止客户端代码看到实现接口的实际类型。

例如,可能有一个接口IReadableMatrix<T>IImmutableMatrix<T>,以及具有只读成员ReadableMatrix<T>,{{ImmutableMatrix<T>int Height的相应结构1}},int WidthT this[int row, int column]。使用ImmutableMatrix<T> AsImmutable();的代码不应该关心它是如何存储的;完全有可能ImmutableMatrix<double>的两个实例可能包含对ImmutableMatrix的不同实现的引用,这些实现在每个单元格中报告相同的内容,但存储的内容完全不同。一个可能是IImmutableMatrix<T>的实例,它包含一个12x12数组,恰好在对角线以外的每个元素中保留零,而另一个可能是ArrayBackedMatrix<double>,并使用12项只存储对角线上的东西的数组(并返回零以响应对任何其他元素的请求)。使用不同类型来存储数组数据应该是一个实现细节,而不是暴露给客户端。

使用结构来包装数组的一个细节是,默认值结构的引用类型字段将为null,但结构本身不会。因此,可能希望结构实现DiagonalMatrix<double>属性,如果支持字段为IsNull则返回true,或者让其他结构成员检查支持字段是否为空如果是这样,则表现为空的0x0矩阵。