【问题标题】:Comparing a list of "Apple" and "Orange" objects比较“Apple”和“Orange”对象的列表
【发布时间】:2019-01-03 05:01:54
【问题描述】:

这更像是一项学术练习,所以我基本上只是想弄清楚当类型不同时如何使用 IComparable。

假设我们有一个水果类,以及派生类“Apple”和“Orange”。假设我想要一个水果列表,让所有的苹果都排在橘子之前。解决这个问题的最佳方法是什么?

我认为你可以让 Fruit 实现接口 IComparable 并为非常子类型放入一堆条件语句,但这对我来说似乎很粗糙并且可能违反了开放/封闭原则。我更感兴趣的是让它以这种方式工作:

public abstract class Fruit : IComparable<Fruit>
{
    public abstract int CompareTo(Fruit other);
}

public class Apple : Fruit, IComparable<Orange>
{
    public override int CompareTo(Fruit other)
    {
        if(other is Orange)
        {
            this.CompareTo((Orange)other);
        }
        return 0;
    }

    public virtual int CompareTo(Orange other)
    {
        return -1;
    }
}

public class Orange : Fruit, IComparable<Apple>
{
    public override int CompareTo(Fruit other)
    {
        if (other is Apple)
        {
            this.CompareTo((Apple)other);
        }
        return 0;
    }

    public virtual int CompareTo(Apple other)
    {
        return 1;
    }
}

我的主要目标是让 IComparable 与跨类型一起工作。我尝试用各种水果加载一个列表,但可惜它没有排序。也许我对 CompareTo 的返回值的理解有点不靠谱。这种方法有什么希望吗?有没有比显而易见的方法更有用的场景?

【问题讨论】:

  • 有没有一种情况,你会看到橙色等于苹果?如果是这样,您将需要 IComparable。如果它们永远不会被认为是相等的,那么简单的类型比较就足够了。
  • 如果你也有香蕉怎么办?那些总是更大的苹果但更小的橙子吗?要么 ...?话虽如此,您的 学术问题 毫无意义,因为您不能假设您的基类只有两个祖先。事实上,基类甚至不应该知道它的祖先的任何事情。
  • 如果目标只是对对象数组进行排序,请考虑使用IEnumerable&lt;T&gt;.OrderByDescending,如var sortedFruits = fruits.OrderByDescending(x =&gt; x is Apple);
  • 冲突的 CompareTo() 实现。 Apple.CompareTo 说苹果总是小于橙子。 Orange.CompareTo 表示橙子总是小于苹果。使用返回 1;在其中一个中再试一次。

标签: c# generics icomparable


【解决方案1】:

我觉得这有点奇怪,因为苹果和橙子没有自然的顺序。 在这个特定的例子中你更喜欢苹果而不是橙子,但也许下一个人想要它反过来。还是冬天的混搭?关键是 Apples 和 Oranges 没有一种独特的排序算法,将它构建到 Apples 或 Oranges 甚至 Fruits 中感觉不对。

这就是IComparer 的用武之地。您可以将比较逻辑放入其中,但您可以有许多 个比较器,并在每次进行排序时选择另一个比较器。所以你在冬天实现了一个ApplesFirstComparer,然后是一个OrangesWithTheMostOrangeColorOnTopDoNotCareForColorOfApplesComparer,一个又一个。基本上,您需要的每个比较都有一个,而不是暗示 Apples 和 Oranges 具有自然顺序。因为他们没有。

【讨论】:

  • 每次比较都是由法币建立的。没有圣经强制规定 A-Z 排序优于 Z-A 排序或 D-C-B-A-EtoZ 排序。您可以先进行“默认”排序,然后再进行特定排序。留在字符串字段中,你有代码点排序,然后你有国家排序规则。
  • @xanatos 当然,但有些事情有一个 defined 顺序,这可能是世界范围内相同的理解。您可以订购日期。或者钱。或衬衫尺码。大多数人会同意并认为这是直观的。无论您选择什么,订购不同种类的水果都不直观。一个人可能会按颜色订购它们,下一个按卡路里订购,两者都可以声称他们是“正确的”。这就是为什么我更喜欢外部比较器的原因。对我来说,这表明这是比较水果的一种方式,而实施的IComparable&lt;&gt;会向我表明存在明显的顺序。
  • 你只需要记录它,你不需要证明它的合理性。我不认为编写各种 DOS 代码页的人证明了字符的位置是合理的 :-) 您在这里应用了“ipse dixit”方法(这是正确的,要清楚,我可能会这样做):如果其他人决定了它(可能是通过法令),那么可以使用它。如果我做同样的事情(决定某事)那就错了。需要明确的是,我唯一的观点是:您可以通过法令决定苹果比猕猴桃更好。有时你必须这样做。重要的是记录。你还是对的。
【解决方案2】:

马上就会变得很糟糕...每个水果都必须知道彼此的水果...如果您有 10 个水果,那么您有 90 段代码只是为了决定如何比较它们。

我会这样做:

public abstract class Fruit : IComparable<Fruit>
{
    // It should be unique for each fruit type
    public abstract int Importance { get; }

    public int CompareTo(Fruit other)
    {
        // If you want, you can do some tests here, that 
        // are common to all the Fruit. I wouldn't,
        // because this would create an ordering with
        // higher priority than Importance.

        int cmp = Importance.CompareTo(other.Importance);

        if (cmp != 0)
        {
            return cmp;
        }

        if (GetType() != other.GetType())
        {
            throw new ApplicationException("Different type of fruit must have different Importance");
        }

        // Other Fruit comparisons

        // We know the Fruit have the same type (see above)
        return CompareToInternal(other);
    }

    // Comparison of subtype of Fruit
    public abstract int CompareToInternal(Fruit other);
}

所以只有同类型的Fruit才真正具有可比性。其他水果有一个预先确定的Importance(苹果比猕猴桃好),还有一个抽象的CompareToInternal来做亚型比较(在同一类型的水果里面......苹果vs苹果,猕猴桃vs猕猴桃)

【讨论】:

    【解决方案3】:

    这是我的想法。这看起来很简单,但它会起作用。

    您可以标记每个类的唯一顺序并对其进行排序。

    public abstract class Fruit
    {
        public int MyOrder {get;}
    }
    
    public class Apple : Fruit
    {    
    }
    
    public class Orange : Fruit
    {    
    }
    

    现在,你想要所有的苹果先于橙子。设置值并排序。

    //Suppose that this is your list fruits
    var fruits = new List<Fruit>();
    fruits.OfType<Apple>().ForEach(a=> a.MyOrder = 1);
    fruits.OfType<Orange>().ForEach(a=> a.MyOrder = 2);
    var sorted = fruits.OrderBy(x=>MyOrder);
    

    如果你有多种水果,会有一个缺点。

    但如果您的订单没有改变,就像苹果总是先于橙子一样。在课堂上设置MyOrder

        public abstract class Fruit
        {
            public abstract int MyOrder {get;}
        }
    
        public class Apple : Fruit
        {    
            public override int MyOrder {
                get { return 1;}
            }
        }
    
        public class Orange : Fruit
        {  
            public override int MyOrder {
                get { return 2;}
            }  
        }
    

    【讨论】:

      猜你喜欢
      • 2021-06-24
      • 1970-01-01
      • 2015-04-02
      • 1970-01-01
      • 2022-06-26
      • 2020-03-05
      • 2013-08-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多