c # укладка разных классов в одном списке - PullRequest
0 голосов
/ 25 ноября 2018

Я изучаю наследование и у меня есть несколько вопросов:

  • Предполагается, что у меня есть базовый класс A.
  • Класс B и класс C являютсяразличные классы, которые наследуются от A.

    1. Если я хочу поместить в один и тот же список экземпляры классов B и C, какой тип списка должен быть?List<object> или List<A>?

    2. Почему List<A> может содержать данные класса B и C, если в классе A меньше полей?Я ожидал бы, что List<A> обрежет дополнительные поля класса B и C внутри списка, но я видел рабочий пример этого.

    3. При циклическом просмотре спискас помощью foreach как узнать, является ли объект классом B или C?

Спасибо!

Ответы [ 3 ]

0 голосов
/ 26 ноября 2018

Его основной oop (объектно-ориентированный прогрэминг) принципал, называемый полиморфизмом

  1. вы должны использовать список List<A>

  2. при вводе B введите объект в List<A> все равно, что смотреть на B как A., чтобы вы могли получить доступ к полям A, которые он наследует

  3. , вы можете использовать оператор is.Например, if (someObject is B)

Вы можете узнать больше здесь Документы полиморфизма

0 голосов
/ 26 ноября 2018
  1. Параметр типа списка должен быть ближайшим общим предком B и C: здесь List<A>.List<object> также будет работать, но за счет того, что он слабо типизирован и позволяет добавлять любые объекты: строки, целые числа, даты, лица.Что, вероятно, не является целью здесь.

  2. Типы классов являются ссылочными типами.Это означает, что список содержит только ссылки на объекты разной длины, которых нет в списке.Т.е. усечения не происходит.Это также причина, по которой типы значений не могут наследоваться.Вы не можете наследовать от int или от структуры типа DateTime.

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

Это называется полиморфизм (многогранный).В этом случае shape может быть Rectangle или Circle.Но вам не нужно заботиться.shape.Area автоматически вызовет Rectangle.Area для Rectangle объектов и Circle.Area для Circle объектов.Это истинная сила ориентации объекта, когда решения с if или switch являются процедурным подходом.

0 голосов
/ 26 ноября 2018

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

Если у вас есть определение Vehicle (это ваше A).Вы могли бы написать программное обеспечение для компании по прокату автомобилей, которая обрабатывает транспортные средства.У них есть все виды транспортных средств, таких как один или несколько велосипедов (это B) и один или несколько автомобилей (это C).Оба очень разные, но оба являются транспортными средствами.

Используя абстракцию Vehicle (A), вы можете хранить велосипеды и автомобили в одном контексте.

Таким образом, в List<Vehicle> вы можете хранить велосипеды, автомобили, грузовики и все виды передвижных вещей.Тем не менее, определение Vehicle будет иметь наименьший общий знаменатель, такой как тип двигателя, количество дверей и т. Д. Но вы (надеюсь) не найдете в этом определении транспортных средств специфических свойств, которые могут не подходить для других, таких каквелосипед или автомобиль.

Отслеживание велосипедов и автомобилей в качестве транспортных средств вообще не снимает поля.Тем не менее, они рассматриваются как транспортные средства, поэтому вы не увидите конкретные свойства, пока не приведете каждое транспортное средство к его реальному типу.Есть несколько подходов к этому, я хотел бы выделить Pattern Matching в 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;
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...