Использование абстрактного класса в качестве параметра в конструкторе базового класса, чтобы иметь более конкретные типы в производных классах - PullRequest
1 голос
/ 04 мая 2019

Исходя из науки о данных, я пытаюсь узнать о работе по разработке программного обеспечения с использованием c #.

Я пытаюсь настроить базовый класс (например, Aliments), из которого несколько других, даже более специфических пользовательских списков классовсобираются вывести (например, фрукты и овощи).На стороне у меня есть отдельные классы фруктов и овощей, которые происходят от Aliment.В конце я хочу, чтобы экземпляры Fruits / Vegs имели список свойств Veg / Fruit (вместе с несколькими свойствами и методами, специфичными для классов Fruits и Vegs).Но я хочу, чтобы это свойство списка было реализовано в базовом классе Aliments вместе с несколькими методами управления им (например, RemoveRipe (), SortBySize () ...)

Я пытался поместить свойство списка вкласс Aliments, а затем заставить производные классы (Fuits / Vegs) вызывать базовый конструктор со списком List или List .

. Я пробовал что-то вроде этого:

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)
    {

    }
}

Основная проблема заключается в том, что список фруктов нельзя преобразовать в список алиментов.Кроме того, весь смысл операции заключается в том, что каждый экземпляр Фруктов имеет Список реальных Фруктов (чтобы я мог получить доступ к определенным методам Фруктов).Есть ли способ, которым я могу это сделать?Я на хорошем пути?Заранее спасибо

PS Извините, если это дубликат, я не совсем понял, как сформулировать проблему, чтобы найти ее

1 Ответ

2 голосов
/ 04 мая 2019

Когда мы говорим, что мы не можем преобразовать List<Fruit> в List<Aliment>, многое зависит от того, что мы подразумеваем под «преобразованием».

Если у вас есть List<Fruit> и вы хотите List<Ailment>, вы можете сделать это, создав новый список. Например, если fruitList является List<Fruit>:

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

Поскольку каждый Fruit является Aliment, вы можете разыграть каждого из них как Aliment и использовать их для заполнения нового 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());

Это не создаст новый список - он просто приведёт список к другому типу. Но если бы мы могли разыграть List<Fruit> как List<Aliment>, то мы могли бы добавить в список любой Aliment, включая Vegetable. Тогда у нас будет Vegetable в нашем List<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 к нему.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...