今天在阅读时,我偶然发现了这个主题:
https://blog.mischel.com/2013/01/05/inheritance-and-iequatable-do-not-mix/
我同意,不实现 IEquatable<T>
是有原因的,因为存在错误实现的可能性。
然而,在阅读了上述链接文章后,我测试了自己在各种非密封、继承类中使用的实现,并发现它是正确的。
在实现 IEquatable<T>
时,我参考了这篇文章:
http://www.loganfranken.com/blog/687/overriding-equals-in-c-part-1/
它很好地解释了在 Equals()
中要使用的代码。虽然它没有涉及继承,但我自己调整了一下。这是结果。
回答原问题:
我不是说它必须在非密封类上实现,但我可以肯定地说,它可以毫无问题地实现。
class CBase : IEquatable<CBase>
{
private int m_iBaseValue = 0;
public CBase (int i_iBaseValue)
{
m_iBaseValue = i_iBaseValue;
}
public sealed override bool Equals (object i_value)
{
if (ReferenceEquals (null, i_value))
return false;
if (ReferenceEquals (this, i_value))
return true;
if (i_value.GetType () != GetType ())
return false;
return Equals_EXEC ((CBase)i_value);
}
public bool Equals (CBase i_value)
{
if (ReferenceEquals (null, i_value))
return false;
if (ReferenceEquals (this, i_value))
return true;
if (i_value.GetType () != GetType ())
return false;
return Equals_EXEC (i_value);
}
protected virtual bool Equals_EXEC (CBase i_oValue)
{
return i_oValue.m_iBaseValue == m_iBaseValue;
}
}
class CDerived : CBase, IEquatable<CDerived>
{
public int m_iDerivedValue = 0;
public CDerived (int i_iBaseValue,
int i_iDerivedValue)
: base (i_iBaseValue)
{
m_iDerivedValue = i_iDerivedValue;
}
public bool Equals (CDerived i_value)
{
if (ReferenceEquals (null, i_value))
return false;
if (ReferenceEquals (this, i_value))
return true;
if (i_value.GetType () != GetType ())
return false;
return Equals_EXEC (i_value);
}
protected override bool Equals_EXEC (CBase i_oValue)
{
CDerived oValue = i_oValue as CDerived;
return base.Equals_EXEC (i_oValue)
&& oValue.m_iDerivedValue == m_iDerivedValue;
}
}
测试:
private static void Main (string[] args)
{
var fooby1 = new Fooby (0, "hello");
var fooby2 = new Fooby (0, "goodbye");
Foo foo1 = fooby1;
Foo foo2 = fooby2;
bool bEqualFooby12a = fooby1.Equals (fooby2);
bool bEqualFooby12b = fooby2.Equals (fooby1);
bool bEqualFooby12c = object.Equals (fooby1, fooby2);
bool bEqualFooby12d = object.Equals (fooby2, fooby1);
bool bEqualFoo12a = foo1.Equals (foo2);
bool bEqualFoo12b = foo2.Equals (foo1);
bool bEqualFoo12c = object.Equals (foo1, foo2);
bool bEqualFoo12d = object.Equals (foo2, foo1);
CBase oB = new CBase (1);
CDerived oD1 = new CDerived (1, 2);
CDerived oD2 = new CDerived (1, 2);
CDerived oD3 = new CDerived (1, 3);
CDerived oD4 = new CDerived (2, 2);
CBase oB1 = oD1;
CBase oB2 = oD2;
CBase oB3 = oD3;
CBase oB4 = oD4;
bool bEqualBD1a = object.Equals (oB, oD1);
bool bEqualBD1b = object.Equals (oD1, oB);
bool bEqualBD1c = oB.Equals (oD1);
bool bEqualBD1d = oD1.Equals (oB);
bool bEqualD12a = object.Equals (oD1, oD2);
bool bEqualD12b = object.Equals (oD2, oD1);
bool bEqualD12c = oD1.Equals (oD2);
bool bEqualD12d = oD2.Equals (oD1);
bool bEqualB12a = object.Equals (oB1, oB2);
bool bEqualB12b = object.Equals (oB2, oB1);
bool bEqualB12c = oB1.Equals (oB2);
bool bEqualB12d = oB2.Equals (oB1);
bool bEqualD13a = object.Equals (oD1, oD3);
bool bEqualD13b = object.Equals (oD3, oD1);
bool bEqualD13c = oD1.Equals (oD3);
bool bEqualD13d = oD3.Equals (oD1);
bool bEqualB13a = object.Equals (oB1, oB3);
bool bEqualB13b = object.Equals (oB3, oB1);
bool bEqualB13c = oB1.Equals (oB3);
bool bEqualB13d = oB3.Equals (oB1);
bool bEqualD14a = object.Equals (oD1, oD4);
bool bEqualD14b = object.Equals (oD4, oD1);
bool bEqualD14c = oD1.Equals (oD4);
bool bEqualD14d = oD4.Equals (oD1);
bool bEqualB14a = object.Equals (oB1, oB4);
bool bEqualB14b = object.Equals (oB4, oB1);
bool bEqualB14c = oB1.Equals (oB4);
bool bEqualB14d = oB4.Equals (oB1);
}
BaseObject:IEquatable<BaseObject>
,而DerivedObject:BaseObject
有一些额外的属性,确保两个仅在该额外属性上不同的DerivedObject
实例互相识别为不同的唯一方法是让IEquatable<BaseObject>
链接到一个虚拟方法(通常是Equals
),然后必须将对象转换为派生类型。 - supercatEquals
方法,在调用虚拟属性测试其他属性之前检查基类属性;这种设计在使用IEquatable<T>
时是安全的,但即使是IEquatable<T>.Equals
也需要检查对象的类型,与使用Equals(Object)
相比并没有太多优势。 - supercat