【问题标题】:IComparable.CompareTo correct implementation considering inheritanceIComparable.CompareTo 正确实现考虑继承
【发布时间】:2018-09-03 09:21:02
【问题描述】:

问题与实体框架和内存排序有关。

阅读本文
https://docs.microsoft.com/en-us/dotnet/api/system.icomparable.compareto?view=netframework-4.7.2
如果类不是同一类型(或者,看起来比较的类不是可比较类的实例),我应该抛出异常。
我使用GetType().IsInstanceOfType(obj)) 来确定是否应该按照API 规范的要求抛出InvalidArgumentException

我有两节课

public class MyClass : IComparable
{
    // ...
}

public class MyClassProxy : MyClass
{
    // This class rappresent an EF proxy
    // ...
}

应用 API 规范,MyClass.CompareTo(MyClassProxy) 应该可以正常工作,而 MyClassProxy.CompareTo(MyClass) 应该不能正常工作。

问题是,在这种情况下,我应该完全尊重 API 定义,还是应该部分释放关于类型的 API 约束,并且如果该类是可比较类的代理,则不抛出异常?

如果我释放约束,检查类型兼容性的正确方法是什么?

【问题讨论】:

  • “应用 API 规范,MyClass.CompareTo(MyClassProxy) 应该可以正常工作” - 这听起来不像。 “obj 与此实例的类型不同。”在这种情况下不正确,因此抛出 ArgumentException, IMO 是有意义的。我会在这里使用GetType() != obj.GetType()
  • @JonSkeet,API 规范中的示例适用于派生类。我认为API规范中is这个词必须缩进为c#is关键字。
  • 我认为这是实现中的一个错误。请注意,稍后它会说:“参数 obj 必须与实现此接口的类或值类型相同;否则,将引发 ArgumentException。”那句话中甚至没有“是”这个词。非对称实现非常令人困惑。

标签: c# entity-framework


【解决方案1】:

我想知道如果MyClass 将实现IComparable<MyClass> 或(或可能以及)IComparable,您是否会有同样的问题

让我们用一个不太抽象的例子来做同样的问题。

假设我们有一个类Animal。每个动物都有一个NumberOfLegs。我们想按这个腿数来订购动物,所以我们实现了IComparable<Animal>

假设我们还有一个类 Human,它派生自 Animal(尽管有些人会对此提出质疑)。每个Human 都有一个Name,我们想按名称订购Humans,所以我们实现IComparable<Human>

同样我们有一个Spider,当然是一个八脚的Animal

class Animal : IComparable<Animal> {...}
class Spider : Animal, IComparable<Spider> {...}
class Human : Animal, IComparable<Human> {...}

以下问题很容易回答:x 和 y 的值是多少?

Animal spider = new Spider();
Animal bill = new Human("William Shakespeare");
int x = spider.CompareTo(bill);  // +1 A spider has more legs than bill
int y = bill.CompareTo(spider);  // -1: bill has less legs than Spider

让我们再做一次:z 的值是多少?

Spider spider = new Spider();
Human bill = new Human("William Shakespeare");
int z = bill.CompareTo(spider); // ???

你想成为什么意思?类Human 没有实现IComparable&lt;Spider&gt;,而且并不是所有的Spider 都有名字。所以我们所能做的就是根据腿的数量进行比较,这正是会发生的事情。

如果您在比较人类和蜘蛛时确实需要特殊处理(例如在恐惧因素上),您应该让 Human 实现 IComparable&lt;Spider&gt;(反之亦然)。

现在您已经确切知道在实施IComparable&lt;Human&gt;IComparable&lt;Animal&gt; 等时会做什么。我想如果您也实施IComparable,您就会知道该怎么做

最后一点:如果您的比较方法是类对象的典型方法,则只为类实现IComparable&lt;...&gt;。也就是说,如果每个人都说您的方法将是排序这些对象的自然方式。

按腿数排序动物不是自然的方式。因此,读者不会本能地知道以下内容会做什么:

IEnumerable<Animal> myAnimals = ...
var result = myAnimals.OrderBy(animal => animal);

Animal 不应该像这样实现 IComparable。我们应该创建一个特殊的 Comparer 类,它在腿数上实现 IComparer&lt;Animal&gt;

class AnimalLegCountCompare: IComparer<Animal> {...}

以下内容不会让读者感到困惑:

ICompared<Animal> legCountComparer = new AnimalLegCountComparer();
var result = myAnimals
   .OrderBy(animal=>animal, legCountComparer);

【讨论】:

  • 感谢您非常准确的回答。这种情况下的问题有点不同。按照您的示例,我有一个实现 IComparable 的 Animal 类。 Entity Framework 在我的 Animal 类上创建了一个代理类,它完全不受我的控制(并且对我的应用程序透明)。让我们称它为 AnimalProxy。现在我有一个包含 Animal 和 AnimalProxy 的集合。通常我应该听从你的建议,但在这种情况下,我认为(但我不确定)代理的创建和代理的使用应该对用户(程序员)完全隐藏。
  • 那么,为什么不创建一个特殊的比较器类来实现IComparer&lt;AnimalProxy&gt;?在该课程中,您可以以任何方式进行比较。唯一的限制是您必须确保如果 A 小于 B,则 B 大于 A。这与所有 StringComparer 类相当。每个 StringComparer 以不同的方式比较字符串。如果需要,您甚至可以创建一个比较人类和蜘蛛的比较器类。
  • 主要问题是第三方组件使用了IComparable。第二个问题是很难创建IComparable&lt;AnimalProxy&gt;,因为它是由实体框架动态生成的,所以我应该动态实现IComparable&lt;AnimalProxy&gt;
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-08-05
  • 1970-01-01
  • 1970-01-01
  • 2014-07-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多