如何比较和排序子类?

时间:2015-08-09 20:58:30

标签: c# sorting compare subclass

例如,我有这些类和子类。

class Program
{
    static void Main(string[] args)
    {
        var animals = new List<Animal> { new Wolf {kills = 5}, 
                                         new Rabbit {name = "Uncle John"}, 
                                         new Eagle {eyeCount = 1}, 
                                         new Wolf {kills = 100}, 
                                         new Rabbit { name = "Human" } };
        animals.Sort();
//Suposted to be: Rabbit(Human), Rabbit(Uncle John), Wolf(5), Wolf(100), Eagle(1)
    }
}

enum SortOrder { Rabbit, Wolf, Eagle }

abstract class Animal{}
class Wolf : Animal
{
    public int kills = 0; //Marked public for simple initialization
}
class Rabbit : Animal
{
    public string name = "Funny Little Guy"; //Marked public for simple initialization
}
class Eagle : Animal
{
    public byte eyeCount = 2; //Marked public for simple initialization
}

我想对动物列表进行排序。 1)在相同类型的其他对象之间进行排序(按名称对狼进行排序,按名称对兔进行排序等)2)在“SortOrder”中对子类组进行排序,因此兔子在列表中排在第一位,老鹰队排在最后。

我曾尝试通过实现IComparable<Animal>, IComparable<Wolf>, IComparable<Rabbit>, IComparable<Eagle>接口来实现这一点,但这导致我无处可去,因为我无法完成这项工作,即使我可以,再添加一个子类会导致大量代码工作。

这是我的尝试:

abstract class Animal : IComparable<Animal>, IComparable<Wolf>, IComparable<Rabbit>, IComparable<Eagle>
{
    public abstract int CompareTo(Animal other);
    public abstract int CompareTo(Wolf other);
    public abstract int CompareTo(Rabbit other);
    public abstract int CompareTo(Eagle other);
}

class Wolf : Animal
{
    public int kills = 0; //Marked public for simple initialization

    public override int CompareTo(Animal other) => other.CompareTo(this);
    public override int CompareTo(Wolf other) => kills.CompareTo(kills);
    public override int CompareTo(Rabbit other) => SortOrder.Wolf.CompareTo(SortOrder.Rabbit);
    public override int CompareTo(Eagle other) => SortOrder.Wolf.CompareTo(SortOrder.Eagle);
}

但通过这种方式,我得到了相反的顺序,而且我说很难添加新的子类。

那么进行这种比较的有效方法是什么?

5 个答案:

答案 0 :(得分:2)

为了最小化添加新代码并强制将新类添加到层次结构中,我将进行这种设计:

首先,我会为每种具体的动物类型实施IComparable。这很简单。其次,我将SortOrder OrderType属性添加到抽象类,并在每个具体实例中实现它。这将迫使任何扩展类的人重新评估枚举并可能为其添加新值。然后,主比较函数将首先检查此属性。如果不相等,则返回类型之间的比较。如果相等,只需在两个实例上调用compare,因为可以安全地假设它们属于同一类型。

在这里实施。我实际上不得不使用反射,因为没有其他方法可以调用具体的比较器。但它似乎工作得非常好,只要实际类型对应于返回的枚举。

enum SortOrder { Rabbit, Wolf, Eagle }

abstract class Animal : IComparable<Animal>
{
    public abstract SortOrder OrderType { get; }

    public int CompareGeneric(Animal x, Animal y)
    {
        // use reflection to call comparer on concrete animal type
        var comparerType = typeof(IComparable<>).MakeGenericType(x.GetType());
        var compareMethod = comparerType.GetMethod("CompareTo");

        return (int)compareMethod.Invoke(x, new object[] { y });
    }

    public int Compare(Animal x, Animal y)
    {
        // clever hack to compare the enums
        var diff = x.OrderType - y.OrderType;
        if (diff != 0)
            return diff;

        return CompareGeneric(x, y);
    }

    public int CompareTo(Animal other)
    {
        return Compare(this, other);
    }
}

class Wolf : Animal, IComparable<Wolf>
{
    public override SortOrder OrderType { get { return SortOrder.Wolf; } }

    public int kills = 0; //Marked public for simple initialization

    public int CompareTo(Wolf other)
    {
        return this.kills.CompareTo(other.kills);
    }
}
class Rabbit : Animal, IComparable<Rabbit>
{
    public override SortOrder OrderType { get { return SortOrder.Rabbit; } }

    public string name = "Funny Little Guy"; //Marked public for simple initialization

    public int CompareTo(Rabbit other)
    {
        return this.name.CompareTo(other.name);
    }
}
class Eagle : Animal, IComparable<Eagle>
{
    public override SortOrder OrderType { get { return SortOrder.Eagle; } }

    public byte eyeCount = 2; //Marked public for simple initialization

    public int CompareTo(Eagle other)
    {
        return this.eyeCount.CompareTo(other.eyeCount);
    }
}

答案 1 :(得分:1)

我认为您可以使用Animal类实现IComparable,并使用只读的SortOrder枚举属性,如Euphoric所述。 然后在每个班级中,您可以覆盖CompareTo方法,将每个物种与其种类进行比较。

    public abstract class Animal : IComparable
    {
        public abstract SortOrder SortOrder { get; }

        public virtual int CompareTo(object obj)
        {
            Animal rightValue = (Animal)obj;
            return this.SortOrder < rightValue.SortOrder ? -1
                : this.SortOrder > rightValue.SortOrder ? 1 : 0;
        }
    }
    public class Wolf : Animal
    {
        public override SortOrder SortOrder { get { return SortOrder.Wolf; } }
        public int kills = 0; //Marked public for simple initialization
        public override int CompareTo(object obj)
        {
            if (obj is Wolf)
            {
                Wolf rightValue = (Wolf)obj;
                return this.kills < rightValue.kills ? -1
                    : this.kills > rightValue.kills ? 1 : 0;
            }
            else
            {
                return base.CompareTo(obj);
            }
        }
    }
    public class Rabbit : Animal
    {
        public override SortOrder SortOrder { get { return SortOrder.Rabbit; } }
        public string name = "Funny Little Guy"; //Marked public for simple initialization

        public override int CompareTo(object obj)
        {
            if (obj is Rabbit)
            {
                Rabbit rightValue = (Rabbit)obj;
                return String.Compare(this.name, rightValue.name);
            }
            else
            {
                return base.CompareTo(obj);
            }
        }
    }
    public class Eagle : Animal
    {
        public override SortOrder SortOrder { get { return SortOrder.Eagle; } }
        public byte eyeCount = 2; //Marked public for simple initialization
        public override int CompareTo(object obj)
        {
            if (obj is Eagle)
            {
                Eagle rightValue = (Eagle)obj;
                return this.eyeCount < rightValue.eyeCount ? -1
                    : this.eyeCount > rightValue.eyeCount ? 1 : 0;
            }
            else
            {
                return base.CompareTo(obj);
            }
        }
    }

答案 2 :(得分:0)

我会在您的Animal类中添加一个SortKey和ICompareable接口,并在派生类中实现逻辑。

    abstract class Animal : IComparable<Animal>
    {
        public abstract int SortKey { get; }

        public abstract int CompareTo(Animal other);
    }
    class Wolf : Animal
    {
        public int kills = 0; //Marked public for simple initialization

        public override int SortKey { get { return 100; } }
        public override int CompareTo(Animal other)
        {
            if (other.SortKey != SortKey)
            {
                return SortKey.CompareTo(other.SortKey);
            }

            var otherWolf = other as Wolf;

            if (otherWolf == null)
            {
                return -1;
            }

            return kills.compareTo(otherWolf.kills);
        }
    }

答案 3 :(得分:0)

考虑在类型中使用IComparer ;并实现IComparable以在特定类型中对进行排序。 Comparer仅对相同类型的比较调用Comparable。

优势(S):

  • 所有类型都不需要知道类型之间的“相对”排序键。此外,基本类型不会带有额外的字段“弄脏”。 (虽然没有什么可以阻止将排序放在这样的'SortKey'字段中。)

  • 订购动物类型的规则在一个地方被隔离,即承包商。

    动物类型中的排序规则已经存在 由IComparable处理,可以覆盖/专门 对于每种类型。

缺点(S):

  • 收集/订购操作需要使用Comparer

  • Comparer添加了“隐藏”逻辑,可能需要针对其他类型进行更新。 (仅当在基本类型中使用类似SortKey的方法时才需要这样做。)

使用单独的IComparaer / IC Comparable方法是使用'SortKey'字段的更通用形式,其他排序混合到IComparable中,因为这样的字段可能被这样的IComparer使用实施

答案 4 :(得分:0)

我会用Linq解决你的问题

using System.Collections.Generic;
using System.Linq;

    public enum SortOrder
    {
        Rabbit,
        Wolf,
        Eagle
    }

    public class Program
    {
        public static void Main(string[] args)
        {
            var animals = (new List<Animal> { new Wolf {Kills = 5},
                                     new Rabbit {Name = "Uncle John"},
                                     new Eagle {EyeCount = 1},
                                     new Wolf {Kills = 100},
                                     new Rabbit { Name = "Human" } })
                            .OrderBy(x => x.SortOrder)
                            .ThenBy(x => x.Classifier);

            //Suposted to be: Rabbit(Human), Rabbit(Uncle John), Wolf(5), Wolf(100), Eagle(1)
        }
    }

    public abstract class Animal
    {
        public abstract object Classifier { get; }

        public string Name { get; set; }

        public SortOrder SortOrder { get; set; }
    }

    public class Eagle : Animal
    {
        public Eagle()
        {
            this.SortOrder = SortOrder.Eagle;
        }

        public override object Classifier
        {
            get { return this.EyeCount; }
        }

        public byte EyeCount { get; set; }
    }

    public class Rabbit : Animal
    {
        public Rabbit()
        {
            this.Name = "Funny Little Guy";
            this.SortOrder = SortOrder.Rabbit;
        }

        public override object Classifier
        {
            get { return this.Name; }
        }
    }

    public class Wolf : Animal
    {
        public Wolf()
        {
            this.Name = "Funny Little Guy";
            this.SortOrder = SortOrder.Wolf;
        }

        public override object Classifier
        {
            get { return this.Kills; }
        }

        public int Kills { get; set; }
    }