C#在同一列表中堆叠不同的类

时间:2018-11-25 20:57:41

标签: c# list inheritance

我正在学习继承并且有几个问题:

  • 假设我有一个基类A。
  • B和类C是继承自A的不同类。

    1. 假设我想堆叠在类BC的相同列表实例中, 列表的类型应该是什么? List<object>List<A>吗?

    2. 如果类List<A>的字段较少,为什么B可以容纳类CA的数据?我希望List<A>将在列表内修剪类BC的多余字段,但我看到了一个有效的示例。

    3. foreach循环列表时,如何确定对象是类B还是C

谢谢!

3 个答案:

答案 0 :(得分:1)

它的基本oop(面向对象的程序设计)原则称为多态性

  1. 您应该使用列表List<A>

  2. 当您将B类型的对象放入List<A>时,它就像将B视为A。这样您就可以访问它继承的A字段

  3. 您可以使用is语句。 例如if (someObject is B)

您可以在这里polymorphisem docs

答案 1 :(得分:1)

  1. 列表的类型参数必须为BC的最接近的共同祖先:此处为List<A>List<object>也可以工作,但是代价是它的类型很弱,并且允许您添加任何类型的对象:字符串,整数,日期,人。这可能不是目的。

  2. 类类型是引用类型。这意味着列表仅包含对不同长度对象的引用,这些对象本身不在列表中。即没有截断发生。这也是无法继承值类型的原因。您不能从intDateTime之类的结构继承。

  3. 让我们为这个问题想象一个更好的例子

public abstract class : Shape
{
}

public class Rectangle : Shape
{
    public double Width { get; set; }
    public double Height { get; set; }
}

public class Circle : Shape
{
    public double Radius { get; set; }
}

现在,让我们计算列表List<shape> shapes中形状的总面积:

double totalArea = 0.0;
foreach (Shape shape in shapes) {
    if (shape is Rectangle rect) {
        totalArea += rect.Width * rect.Height;
    } else if (shape is Circle circle) {
        totalArea += circle.Radius * circle.Radius * Math.Pi;
    }
}

double totalArea = 0.0;
foreach (Shape shape in shapes) {
    switch (shape)
    {
        case Rectangle rect:
            totalArea += rect.Width * rect.Height;
            break;
        case Circle circle:
            totalArea += circle.Radius * circle.Radius * Math.Pi;
            break;
    }
}

但是通常,如果您不需要知道类型,那就更好。例如。不用在上一个示例中使用switch,而是让类自己完成这项工作

public abstract class : Shape
{
    public abstract double Area { get; }
}

public class Rectangle : Shape
{
    public double Width { get; set; }
    public double Height { get; set; }

    public override double Area => Width * Height;
}

public class Circle : Shape
{
    public double Radius { get; set; }

    public override double Area => Radius * Radius * Math.Pi;
}

然后循环变成

double totalArea = 0.0;
foreach (Shape shape in shapes) {
    totalArea += shape.Area;
}

这称为polymorphism(多形)。在这种情况下,shape可以是RectangleCircle。但是您不必在意。 shape.Area将自动调用Rectangle.Area对象的RectangleCircle.Area对象的Circle。这就是面向对象的真正威力,其中ifswitch的解决方案是一种过程方法。

答案 2 :(得分:0)

如果您有List<A>,则可以将其用于A类型的每个类以及所有派生类。让我们将其带入现实世界:

如果您有一个定义Vehicle(即您的A)。您可以为处理车辆的租车公司编写软件。它们具有各种类型的车辆,例如一辆或多辆自行车(即B)和一辆或多辆汽车(即C)。两者都有很大的不同,但都是车辆。

通过使用VehicleA)的抽象,您可以在相同的上下文中存储自行车和汽车。

因此,在List<Vehicle>中,您可以存储自行车,汽车,卡车和各种活动物品。但是,定义Vehicle的分母最少,例如电动机的种类,门的数量等。但是(希望)您在该定义的车辆上找不到卡车专用的属性,可能不适合其他诸如自行车或汽车。

将自行车和小汽车作为车辆进行跟踪并不会完全脱离领域。但是,它们被视为载具,这就是为什么直到将每个载具转换为实型之前您都看不到特定的属性。有一些解决方法,我想强调一下C#7中的模式匹配,到目前为止,这真的很容易。

foreach (var v in vehicleList)
{
    switch (v)
    {
        case Bike b:
            // ... do bike specific things
            break;
        case Car c:
            // ... do car specific things
            break;
        case Truck t:
            // ... do truck specific things
            break;
        default:
            WriteLine("<unknown>");
            break;
    }
}