将Child类添加到List <parent>时,特定于子项的数据是否丢失?

时间:2016-06-07 20:01:05

标签: c# inheritance

我有一个功能,列出动物,其中一些是狗,其中一些只是动物。该函数将该列表返回给想要仅提取狗的不同函数,这是可能的,因为Animal类有一个bool isDog。

我的问题是,是否可以从动物(父)类中提取特定于狗的(特定于孩子的)数据。一只动物还能成为引擎盖下的狗吗?

我尝试过的事情:

  • 使用isDog bool抽出狗,然后叫狗 构造函数在返回函数的函数之外创建一个新函数 动物清单
  • 给狗类一个接收动物的构造函数,但仍然 没办法保留尾巴长度

我有这两个接口

public interface IAnimal
{
    string eyeColor;
    int numLegs;
}

public interface IDog: IAnimal
{
    double tailLength;
}

和这两个实现

public class Animal: IAnimal
{
    public string eyeColor{get;set}
    public int numLegs {get; set;}
    public bool isDog;

    public Animal(string eyes, int legs){
        this.eyeColor = eyes;
        this.numLegs = legs
        this.isDog = false;
    }
}

public class Dog: Animal, IDog
{
    public double tailLength {get;set;}
    public Dog(double tail, string eyes)
    {
        this.tailLength = tail;
        this.eyeColor = eyes;
        this.numLegs = 4;
        this.isDog = true;
    }
}

这是两个函数的当前状态

List<Animal> getAnimals()
{
    List<Animal> animals;
    Animal a1 = new Animal("Blue", 4);
    animals.Add(a1);
    Animal a2 = new Animal("Green", 2);
    animals.Add(a2);
    Dog d1 = new Dog(2.3, "Brown");
    animals.Add(d1);
    Dog d2 = new Dog(3.5, "Black");
    animals.Add(d2);
    return animals;
}

void foo(){
    IEnumerable<IAnimal> sample = getAnimals();
    var x = totalTailLength(sample.Where(d => d.isDog));
}

double totalTailLength(IEnumerable<IDog> dogs){
    return dogs.Sum(d => d.tailLength);
}

由于

4 个答案:

答案 0 :(得分:5)

不,当然,特定于子类的数据不会丢失。

但请:

  • 将您的实例变量定义为私有并提供公共属性
  • 删除变量isDog!这是您的概念中的错误。明天,如果你创建类Cat,Mouse,Duck,你会添加实例变量isCat,isMouse,isDuck吗?

如果您只需要提取狗类动物,您只需使用Linq:

IEnumerable<IDog> dogs = animals.OfType<IDog>();

答案 1 :(得分:5)

你不能这样做。这样:

sample.Where(d => d.isDog)

将返回IEnumerable<Animal>,因为那是您的收藏中的类型。数据不会丢失,但您正在使用Animal类型,但您不了解Dog属性。

您希望实际从您的收藏中获取Dog类型,例如:

sample.OfType<Dog>()

如果调试代码,可以在调试器中查看运行时的基础属性。尝试一下,您就可以看到集合中的真实类型。

答案 2 :(得分:5)

是的,你绝对可以这样做。如果您使用确切的代码并修复了一些错误,那么您可以看到它有效:

  1. 错过分号有两种语法错误。

  2. 字段不能是接口成员,因此您需要将它们设为属性。

  3. Dog需要调用父的构造函数,这也无需手动设置这些属性:

    public Dog(double tail, string eyes) : base(eyes, 4)
    {
        this.tailLength = tail;
        this.isDog = true;
    }
    
  4. getAnimals中,您需要初始化列表:

    List<Animal> animals = new List<Animal>();
    
  5. foo中,sample的类型为IEnumerable<IAnimal>,因此元素的类型为IAnimal。但是,该类型没有isDog属性(因为它在类上,但在接口上没有)。因此,要么将类型更改为IEnumerable<Animal>,要么将成员移动到界面。

  6. totalTailLength预计会有IDogsample.Where(),但IAnimal会返回Animal s(或var x = totalTailLength(sample.Where(d => d.isDog).Cast<IDog>()); s,如上所述)。由于您过滤了元素,因此应该强制使用可枚举:

    Dog
  7. 然后一切都会编译 - 如果你真的打印出结果 - 你会得到正确的结果。

    对象的类型不会附加到引用该对象的变量,而是附加到对象本身。因此,动物清单中的每个isDog仍然知道它是一只狗。这实际上允许您摆脱IAnimal someAnimal = new Dog(); bool isADog = someAnimal is Dog; // true 成员,因为您可以使用is operator检查对象是否属于特定类型:

    Dog

    您还可以使用LINQ来使用Enumerable.OfType<T>过滤IAnimal列表的var x = totalTailLength(sample.OfType<IDog>()); 元素:

    FOO

答案 3 :(得分:3)

没有数据丢失。这堂课不会成为狗。您所要做的就是将其强制转换为子类型,数据将在那里等着您。