【问题标题】:When a Child class is added to a List<parent>, is the child-specific data lost?将 Child 类添加到 List<parent> 时,特定于子级的数据会丢失吗?
【发布时间】:2016-06-07 20:01:05
【问题描述】:

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

我的问题是,是否可以从动物(父)类中提取特定于狗的(特定于儿童)数据。动物在引擎盖下还能是狗吗?

我尝试过的事情:

  • 使用 isDog 布尔提取狗,然后调用 Dog 构造函数在返回 a 的函数之外创建一个新的 动物名单
  • 为狗类提供一个构造函数,该构造函数接受动物,但仍然 没有办法保持尾巴长度

我有这两个接口

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);
}

谢谢

【问题讨论】:

  • 您可能想看看 C# 中的“is”和“as”关键字,这在测试类型或类型之间的转换时非常有用
  • 如所写,您的代码甚至无法编译!
  • 另外,您不尊重 .NET 命名法(函数和属性的帕斯卡大小写)...

标签: c# inheritance


【解决方案1】:

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

  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&lt;IAnimal&gt;,因此元素的类型为IAnimal。然而,该类型没有isDog 属性(因为它在类上但不在接口上)。所以要么把类型改成IEnumerable&lt;Animal&gt;,要么把成员移到接口上。

  6. totalTailLength 期望IDogs 的可枚举但sample.Where() 返回IAnimals(或Animals 如上所述)。由于您过滤了元素,因此您应该强制转换可枚举:

    var x = totalTailLength(sample.Where(d => d.isDog).Cast<IDog>());
    

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

对象的类型不附加到引用该对象的变量,而是附加到对象本身。所以你的动物列表中的每个Dog 仍然知道它是一只狗。这实际上允许您摆脱 isDog 成员,因为您可以使用 is operator 检查对象是否属于特定类型:

IAnimal someAnimal = new Dog();
bool isADog = someAnimal is Dog; // true

您还可以将其与 LINQ 一起使用,使用 Enumerable.OfType&lt;T&gt; 过滤您的 IAnimal 列表中的 Dog 元素:

var x = totalTailLength(sample.OfType<IDog>());

【讨论】:

  • 感谢 poke,第一次使用 C#,这很有帮助
【解决方案2】:

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

但是请:

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

如果你只需要提取 Dog 动物,你可以简单地使用 Linq :

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

【讨论】:

    【解决方案3】:

    你不能那样做。这个:

    sample.Where(d => d.isDog)
    

    将返回一个IEnumerable&lt;Animal&gt;,因为这就是您集合中的类型。数据没有“丢失”,但您正在使用 Animal 类型,这些类型不知道您的 Dog 属性。

    您希望真正从您的集合中获取 Dog 类型,例如:

    sample.OfType<Dog>()
    

    如果您调试代码,您可以在运行时在调试器中查看底层属性。试一试,您就可以在您的收藏中看到真正的类型。

    【讨论】:

      【解决方案4】:

      没有数据丢失。班级并没有停止成为狗。您所要做的就是将其转换回其子类型,数据就会在那里等着您。

      【讨论】:

        猜你喜欢
        • 2021-11-17
        • 1970-01-01
        • 1970-01-01
        • 2018-03-12
        • 2010-11-13
        • 1970-01-01
        • 2021-01-04
        • 1970-01-01
        • 2014-04-05
        相关资源
        最近更新 更多