首页 > 解决方案 > 在基类构造函数中使用抽象类作为参数以在派生类中具有更具体的类型

问题描述

来自数据科学,我正在尝试使用 c# 学习 oop 进行软件开发。

我正在尝试建立一个基类(例如 Aliments),其他一些甚至更具体的自定义列表类将从中派生(例如 Fruits 和 Vegs)。另一方面,我有从 Aliment 派生的单独的 Fruit 和 Veg 类。最后,我希望 Fruits/Vegs 的实例有一个 Veg/Fruit 属性列表(以及一些特定于 Fruits 和 Vegs 类的属性和方法)。但我希望在 Aliments 基类中实现这个列表的适当性以及一些管理它的方法(例如 RemoveRipe()、SortBySize()...)

我尝试在 Aliments 类中放置一个 List 属性,然后让派生类(Fuits/Vegs)使用 List<Fruit> 或 List<Veg> 调用基本构造函数。

我试过这样的事情:

public abstract class Aliment
{
    public int Ripeness;
}

public class Fruit : Aliment
{
    // be a fruit
}

public abstract class Aliments
{
    public Aliments(List<Aliment> alimentList)
    {
        AlimentList = alimentList;
    }

    public List<Aliment> AlimentList { get; private set; }

    public void RemoveRipe()
    {
        //...
    }
}

public class Fruits : Aliments
{
    public Fruits(List<Fruit> fruitList)
        : base(fruitList)
    {

    }
}

主要问题是水果列表无法转换为食物列表。此外,操作的重点是每个 Fruits 实例都有一个实际 Fruit 的 List(以便我可以访问 Fruits 的特定方法)。有什么办法可以做到吗?我走的好吗?提前致谢

PS对不起,如果它是重复的,我真的不知道如何制定问题以便搜索它

标签: c#oopinheritance

解决方案


当我们说我们不能将 a 转换List<Fruit>为 aList<Aliment>时,很大程度上取决于我们所说的“转换”是什么意思。

如果您有 aList<Fruit>并且想要 a List<Ailment>,则可以通过创建一个新列表来实现。例如,如果fruitListList<Fruit>

var alimentList = new List<Aliment>(fruitList.Cast<Aliment>())

因为everyFruit 一个Aliment,所以您可以将它们中的每一个都转换为anAliment并使用它们来填充一个新的List<Aliment>

如果你这样做了,你的Fruits构造函数将编译:

public class Fruits : Aliments
{
    public Fruits(List<Fruit> fruitList)
        : base(new List<Aliment>(fruitList.Cast<Aliment>()))
    { }
}

我们不能做的是a转换List<Fruit>List<Aliment>.

为什么?因为如果可以的话,我们可以这样做:

var alimentList = (List<Aliment>)fruitList;
alimentList.Add(new Vegetable());

这不会创建一个新列表 - 它只会将列表转换为不同的类型。但是,如果我们可以将a 转换List<Fruit>为 aList<Aliment>那么我们可以将任何添加Aliment到列表中,包括 a Vegetable。然后我们VegetableList<Fruit>.

这对我们来说并不总是显而易见的,但编译器会立即发现并阻止它。

由于这个问题,如果您使用我上面显示的代码来修复构造函数中的错误,您仍然会遇到完全相同的问题。Fruits仍然会有一个List<Aliment>继承自的属性Aliments,所以你仍然可以这样做:

var fruits = new Fruits(someListOfFruit);
fruits.AlimentList.Add(new Vegetable());

这不会给你一个编译器错误,但它显然仍然不是你想要的行为。

可能对您有所帮助的是这样的通用类:

public abstract class Aliments<T> where T : Aliment
{
    public Aliments(List<T> alimentList)
    {
        AlimentList = alimentList;
    }
    public List<T> AlimentList { get; private set; }
}

它允许您执行此操作。您可以定义一个继承自Aliments但指定实际类型的类Aliment

public class Fruits : Aliments<Fruit>
{
    public Fruits(List<Fruit> alimentList) : base(alimentList)
    {
    }
}

通过指定Fruit泛型参数T,您的AlimentList属性现在定义为List<Fruit>. 编译器只会让你添加Fruit它。


推荐阅读