在具有不同继承相关类型的 T 的列表 <T> 中使用 IComparable

Using IComparable in list<T> with different inheritance related types of T

这里只是一些示例 class。我尝试在通用列表中使用多态性,所以我希望每个列表都使用自己的 CompareTo-Method。我不想找到不同的 class 安排。我想了解为什么这不起作用。

class Program
{
    static void Main(string[] args)
    {

        List<Animal> MyAnimals = new List<Animal>();
        MyAnimals.Add(new Dog() { Name = "karl", Number = 1 });
        MyAnimals.Add(new Dog() { Name = "carla", Number = 2 });
        MyAnimals.Add(new Dog() { Name = "loki", Number = 3 });
        MyAnimals.Add(new Cat() { Name = "karsten", Size = 3 });
        MyAnimals.Add(new Cat() { Name = "charlie", Size = 5 });
        MyAnimals.Add(new Cat() { Name = "mona", Size = 1 });
        MyAnimals.Add(new Cat() { Name = "sisi", Size = 2 });
        MyAnimals.Sort();
        ShowList(MyAnimals);
        List<Cat> cats = new List<Cat>();
        List<Dog> dogs = new List<Dog>();

        foreach (var item in MyAnimals)
        {
            if (item is Cat)
                cats.Add(item as Cat);
            if (item is Dog)
                dogs.Add(item as Dog);
        }

        dogs.Reverse();
        dogs.Sort();
        cats.Sort();
        ShowList(dogs);
        ShowList(cats);

        Console.ReadKey();

    }

    private static void ShowList<T>(List<T> MyAnimals)
    {
        Console.WriteLine("List of "+MyAnimals[0].GetType().Name);
        foreach (var item in MyAnimals)
        {
            Console.WriteLine(item);
        }
    }
}

abstract class Animal  : IComparable{
    public string Name { get; set; }

    public int CompareTo(object obj)
    {
        if (obj == null) return 1;

        Animal animal = obj as Animal;
        if (animal != null)
            return this.Name.CompareTo(animal.Name);
        else
            throw new ArgumentException("Object is not a Animal");
    }

    public override string ToString()
    {
        return Name;
    }
}
class Dog : Animal, IComparable
{
    public int Number { get; set; }
    public override string ToString()
    {
        return base.ToString() + "\tNumber:"+ Number + "\n";
    }
    public int CompareTo(object obj)
    {
        if (obj == null) return 1;

        if (obj is Dog animal)
            if (this.Number.CompareTo(animal.Number) == 0)
            {
                return this.Name.CompareTo(animal.Name);
            }
            else
            {
                return this.Number.CompareTo(animal.Number);
            }
        else
            throw new ArgumentException("Object is not a Dog");

    }
}

class Cat : Animal, IComparable
{
    public int Size { get; set; }
    public override string ToString()
    {
        return base.ToString() + "\tNumber:" + Size + "\n";
    }

    public new int CompareTo(object obj)
    {
        if (obj == null) return 1;

        if (obj is Cat animal)
            if (this.Size.CompareTo(animal.Size) == 0)
            {
                return this.Name.CompareTo(animal.Name);
            }
            else
            {
                return this.Size.CompareTo(animal.Size);
            }
        else
            throw new ArgumentException("Object is not a Cat");

    }
}

为什么 MyAnimals.Sort() 不使用 Animal class 中的 CompareTo()?有没有一种方法可以像预期的那样使用多态性?所以动物列表是通过 AnimalMethod 比较的,狗列表是通过 Dogs CompareTo 方法等等?

当使用 IComparable 对列表进行排序时。被调用的 CompareTo 方法始终是在对象本身的类型上定义的方法,而不是在您为列表定义的类型上定义的方法。因为 CatDog 覆盖 CompareTo 因此 Sort 方法永远不会在 Animal.

中使用 CompareTo

你可以做的是使用重载 public void Sort (Comparison<T> comparison):

private int CompareAnimals(Animal animA, Animal animB)
{
    // ... your logic to compare two Animals
}

private int CompareCats(Cat catA, Cat catB)
{
    // ... your logic to compare two Cats
}

private int CompareDogs(Dog dogA, Dog dogB)
{
    // ... your logic to compare two Dogs
}

static void Main(string[] args)
{
    List<Animal> MyAnimals = new List<Animal>();
    // Adding animals...
    MyAnimals.Sort(CompareAnimals);
    // ...
    dogs.Sort(CompareDogs);
    cats.Sort(CompareCats);
}

这里的问题是 DogCat classes 有一个 CompareTo 方法 隐藏 base-class 方法,因为 Sort 将使用项目实际类型的 ComapareTo 方法,而不是为 [=17] 上的项目定义的类型=], 你会在尝试比较 DogCat.

时得到一个异常

解决这个问题的一种方法是改为实现 IComparable<T> 接口,以便我们指定要与对象进行比较的确切类型。这样,Sort 方法将使用正确的实现。

另请注意,对于每个派生的 class,我们可以代替 re-implementing AnimalCompareTo 方法(比较 Name 属性)只需在必要时从派生的 class 的 CompareTo 方法中调用 base.CompareTo(other)

abstract class Animal : IComparable<Animal>
{
    public string Name { get; set; }

    public int CompareTo(Animal other)
    {
        if (other == null) return 1;
        return string.Compare(Name, other.Name);
    }

    public override string ToString()
    {
        return Name;
    }
}

class Dog : Animal, IComparable<Dog>
{
    public int Number { get; set; }

    public override string ToString()
    {
        return base.ToString() + "\tNumber:" + Number + "\n";
    }

    public int CompareTo(Dog other)
    {
        if (other == null) return 1;
        var numberCompare = Number.CompareTo(other.Number);
        return numberCompare == 0 ? base.CompareTo(other) : numberCompare;
    }
}

class Cat : Animal, IComparable<Cat>
{
    public int Size { get; set; }

    public override string ToString()
    {
        return base.ToString() + "\tNumber:" + Size + "\n";
    }

    public int CompareTo(Cat other)
    {
        if (other == null) return 1;
        var sizeCompare = Size.CompareTo(other.Size);
        return sizeCompare == 0 ? base.CompareTo(other) : sizeCompare;
    }
}

此外,您可能希望更改 ShowList 方法以显示类型 T 而不是列表中第一个元素的派生类型(否则 List<Animal> 显示这是 List<Dog> 因为第一项是 Dog):

private static void ShowList<T>(List<T> MyAnimals)
{
    Console.WriteLine("List of " + typeof(T).Name);

    foreach (var item in MyAnimals)
    {
        Console.WriteLine(item);
    }
}