任何人对IEquatable<T>
或IComparable<T>
是否通常应要求T
为sealed
(如果是class
)有任何意见?
因为我正在编写一组旨在帮助实现不可变类的基类,所以我想到了这个问题。基类打算提供的部分功能是自动实现相等性比较(使用类的字段以及可以应用于字段以控制相等性比较的属性)。完成后应该会很不错-我正在使用表达式树为每个T
动态创建一个已编译的比较函数,因此比较函数应该非常接近常规相等比较函数的性能。 (我使用的是基于System.Type
的不可变字典,并进行了双重检查锁定,以合理的方式存储生成的比较函数)
但是出现的一件事是,用于检查成员字段是否相等的功能。我最初的目的是检查每个成员字段的类型(我将其称为X
)是否实现IEquatable<X>
。但是,经过一番思考,除非X
是sealed
,否则我认为这是不安全的。原因是如果X
不是sealed
,我不确定X
是否适当地将相等性检查委托(delegate)给X
上的虚拟方法,从而允许子类型重写相等性比较。
然后,这提出了一个更笼统的问题-如果类型不是密封的,那么它真的应该全部实现这些接口(interface)吗?我认为不会,因为我认为接口(interface)协定是在两种X
类型之间进行比较,而不是两种可能不是X
的类型(尽管它们当然必须是X
或子类型)。
你们有什么感想?对于未密封的类,应避免使用IEquatable<T>
和IComparable<T>
吗? (这也让我想知道是否有fxcop规则)
我当前的想法是让生成的比较函数仅在IEquatable<T>
为T
的成员字段上使用sealed
,并且即使Object.Equals(Object obj)
实现T
,也可以在未密封T
的情况下使用虚拟IEquatable<T>
,因为该字段可能存储了T
的子类型,因此我怀疑IEquatable<T>
的大多数实现都是为继承而适当设计的。
最佳答案
我一直在考虑这个问题,经过一番考虑之后,我同意实现IEquatable<T>
和IComparable<T>
只能在密封类型上完成。
我来回走了一段时间,但随后想到了以下测试。以下情况在什么情况下应返回false?恕我直言,2个对象相等或不相等。
public void EqualitySanityCheck<T>(T left, T right) where T : IEquatable<T> {
var equals1 = left.Equals(right);
var equals2 = ((IEqutable<T>)left).Equals(right);
Assert.AreEqual(equals1,equals2);
}
假定比较器为等效类型,则给定对象上
IEquatable<T>
的结果应具有与Object.Equals
相同的行为。在对象层次结构中两次实现IEquatable<T>
可以并且有两种隐含的方式在系统中表达相等性。很容易想到IEquatable<T>
和Object.Equals
会有所不同的任何情况,因为存在多个IEquatable<T>
实现,但只有一个Object.Equals
。因此,以上操作将失败并在您的代码中造成一些困惑。有人可能会争辩说,在对象层次结构的较高位置实现
IEquatable<T>
是有效的,因为您想比较对象属性的子集。在这种情况下,您应该选择专门用于比较这些属性的IEqualityComparer<T>
。