设计避免必须将List <derived>转换为List <base />

时间:2017-06-17 09:01:56

标签: c#

我正在开发一个应用程序。为了论证,让我们说应用程序代表动物园。每个动物园的数据存储在List <Zoo>类型变量中,其中Zoo定义为:

public class Zoo {
    List<Animal> animals;
    ...
}

Animal定义为:

public abstract class Animal {
    public virtual string Name {get; set;};

    ...
}

各种类派生自Animal

public class Dog {
    public int nLegs = 4;
    public string furColour= "White";
    public Dog() {
        Name = "Dog";
    }
    ...
}

public class Millipede {
    public int nLegs = int.maxValue;
    public Millipede() {
        Name = "Millipede";
    }
    ...
}

public class Goldfish {
    public string colour = "Red";
    public Goldfish() {
        Name = "Goldfish";
    }
    ...
}

假设我们生活在一个疯狂的世界中,每个Zoo只能有一种类型的Animal(但是Animal只有他们想要的许多不同的实例。一个特别Zoo非常喜欢Millipedes

List<Animal>.Add(new Millipede());
List<Animal>.Add(new Millipede());
List<Animal>.Add(new Millipede());
List<Animal>.Add(new Millipede());

另一个Zoo非常喜欢金鱼:

List<Animal>.Add(new Goldfish());
List<Animal>.Add(new Goldfish());
List<Animal>.Add(new Goldfish());
List<Animal>.Add(new Goldfish());

理论上,Animal类可以是任何东西,我无法知道不同的实现会是什么样的,但具体的Zoo类会。理想情况下,我想使用List<Dog>List<Millipede>,而不是List<Animal>,因此我可以访问Animal子类的特定属性/方法。我遇到了Convert List<DerivedClass> to List<BaseClass>,其中列出了一些选项,并说明了为什么将List<Animal>投射到List<Dog>没有多大意义。在我的特定应用程序中,这种转换可能发生在许多列表中,每个列表包含大量项目(1000s),每个列表每秒多次,因此我不可能每次都进行转换。

对于上面这样的例子,组织类结构的最佳方法是什么,这样可以完全避免这个问题?

2 个答案:

答案 0 :(得分:2)

这可能就是你所需要的:

public class Zoo<T> where T : Animal
{
    public List<T> animals = new List<T>();
}

public abstract class Animal
{
    public virtual string Name {get; set;}
}

public class Dog : Animal
{
    public int nLegs = 4;
    public string furColour= "White";
    public Dog()
    {
        Name = "Dog";
    }
}

public class Millipede : Animal
{
    public int nLegs = int.MaxValue;
    public Millipede()
    {
        Name = "Millipede";
    }
}

public class Goldfish : Animal
{
    public string colour = "Red";
    public Goldfish()
    {
        Name = "Goldfish";
    }
}

现在你可以这样做:

var zoo = new Zoo<Dog>();
zoo.animals.Add(new Dog());
zoo.animals.Add(new Dog());
zoo.animals.Add(new Dog());

答案 1 :(得分:-1)

我认为您最好的选择是尝试追踪您计划使用的每种类型对象的常见行为。在动物集合的情况下(基于您的示例),这可能相当容易,但如果物体彼此之间存在很大差异(汽车,计算机外围设备,建筑材料和食物),则几乎不可能。

如果您设法在它们之间找到行为的相似之处,那么您最好的选择是使用一般界面,该界面应该由您计划使用的每个对象实现。

根据您的示例,您可以添加以下内容:

public enum AnimalGroupEnum
{
    Amphibian,
    Bird,
    Fish,
    Invertebrate,
    Mammal,
    Reptile,
}

连同界面:

public interface IAnimal
{
    AnimalGroupEnum AnimalGroup { get; }
} 

然后你的Zoo班级成为:

public class Zoo 
{
    List<IAnimal> Animals
}

定义的所有动物(DogMillipedeGoldfish)都应实现此界面,以便能够将其添加到Animals集合中。但是,执行此操作时,只有在查看集合时才能访问AnimalGroup属性。当然,这个界面可能包含您需要从对象中获得的其他东西。

希望这能为你的问题提供一些启示。