IEquatable <T>,IComparable <T>应该在非密封类上实现吗?

Phi*_*hil 36 c# icomparable immutability sealed iequatable

任何人对是否有任何意见IEquatable<T>IComparable<T>一般应要求Tsealed(如果是class)?

这个问题发生在我身上,因为我正在编写一组旨在帮助实现不可变类的基类.基类要提供的部分功能是自动实现相等比较(使用类的字段以及可应用于字段来控制相等比较的属性).当我完成时它应该是相当不错的 - 我正在使用表达式树为每个动态创建一个编译的比较函数T,因此比较函数应该非常接近正则相等比较函数的性能.(我使用键入的不可变字典System.Type和双重检查锁定以合理的方式存储生成的比较函数)

尽管如此,有一件事是用来检查成员字段相等性的函数.我的初衷是检查每个成员字段的类型(我将调用X)是否实现IEquatable<X>.但是,经过一番思考后,除非X是这样,否则我认为这是不安全的sealed.原因在于,如果X不是sealed,我无法确定是否X正确地将等式检查委托给虚拟方法X,从而允许子类型覆盖相等比较.

这就提出了一个更普遍的问题 - 如果一个类型没有被密封,它是否应该真正实现这些接口?我想不会,因为我认为接口契约是比较两种X类型,而不是两种类型,可能是也可能不是X(虽然它们当然必须是X或者是子类型).

你们有什么感想?应该IEquatable<T>IComparable<T>避免对开封类?(也让我想知道是否有一个fxcop规则)

我现在的想法是让我产生比较功能只能用IEquatable<T>在成员字段,其Tsealed,而是用虚拟的Object.Equals(Object obj),如果T是密封的,即使T工具IEquatable<T>,因为该领域可能的潜在店亚型T和我怀疑的大多数实现IEquatable<T>适当设计的传承.

Jar*_*Par 20

我一直在考虑这个问题,经过一些考虑,我同意实施IEquatable<T>,IComparable<T>只应在密封类型上完成.

我来回走了一会儿然后我想到了下面的测试.在什么情况下,以下应该返回虚假?恕我直言,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);
}
Run Code Online (Sandbox Code Playgroud)

对于IEquatable<T>给定对象的结果应该具有与Object.Equals假设比较器是等效类型相同的行为.IEquatable<T>在对象层次结构中实现两次允许并且暗示在系统中有两种不同的表达相等的方式.这很容易图谋任意数量的场景中IEquatable<T>,并Object.Equals会不同,因为有多个IEquatable<T>实现,但只有一个Object.Equals.因此,上述操作会失败并在您的代码中造成一些混乱.

有些人可能会争辩说,IEquatable<T>在对象层次结构中的更高点实现是有效的,因为您想要比较对象属性的子集.在这种情况下,您应该支持IEqualityComparer<T>专门用于比较这些属性的.

  • 正是贾里德。我想补充一下您所说的,如果您尝试以继承安全的方式实现 IEquatable&lt;T&gt; ,则必须提供一个虚拟 bool IsEqual(T obj) 方法,并要求每次都由子类型,子类型的重写首先调用 base.IsEqual(T obj) 并在基方法返回 false 时返回 false。当然,然后你看看它,并问自己考虑到 virtual object.Equals(object obj) 的存在,也许你可以用它来代替。你对第一个子类型进行额外的施法命中,但无论如何,其他后代都必须施法。 (2认同)

sup*_*cat 5

我通常建议不要在任何非密封类上实现IEquatable <T>,或者在大多数情况下实现非通用IComparable,但对于IComparable <T>则不能这样说.两个原因:

  1. 已经存在一种比较可能是或可能不是同一类型的对象的方法:Object.Equals.由于IEquatable <T>不包含GetHashCode,因此其行为必须与Object.Equals的行为相匹配.除了Object.Equals之外,实现IEquatable <T>的唯一原因是性能.与适用于密封类类型的Object.Equals相比,IEquatable <T>提供了较小的性能提升,并且在应用于结构类型时有了很大的改进.未密封类型的IEquatable <T> .Equals实现的唯一方法是确保其行为与可能被覆盖的Object.Equals的行为匹配,但是,调用Object.Equals.如果IEquatable <T> .Equals必须调用Object.Equals,任何可能的性能优势都会消失.
  2. 对于基类来说,有时可能的,有意义的和有用的是具有仅涉及基类属性的定义的自然顺序,这些属性将通过所有子类保持一致.检查两个对象是否相等时,结果不应取决于是将对象视为基类型还是派生类型.但是,在对对象进行排名时,结果通常取决于用作比较基础的类型.派生类对象应实现IComparable <TheirOwnType>,但不应覆盖基类型的比较方法.当作为父类型进行比较时,两个派生类对象比较为"未分配"是完全合理的,但是当作为派生类型进行比较时,要比较另一个对象.

在可继承类中实现非泛型IComparable可能比IComparable <T>的实现更值得怀疑.可能最好的做法是允许基类实现它,如果不期望任何子类需要一些其他排序,但子类不重新实现或覆盖父类实现.