Классы и объекты C#: абстрактные классы

уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.

Иногда бывает необходимо создавать классы, экземпляры которого нельзя создавать. Такие классы называются абстрактными. В каком случае нам может потребоваться абстрактный класс? Например, в случае, когда набор каких-либо сущностей относятся к одной и той же области знаний, но могут иметь различные реализации одних и тех же свойств и методов. Для примера, представим себе такую задачу — нам необходимо разработать систему учёта сотрудников в учебном заведении. Студент и преподаватель — это два человека у которых может быть имя, фамилия. При этом у преподавателя есть звание, должность, какие-то свои характеристики, например, стаж работы. У студента тоже могут быть свои, характерные только для него свойства, например, курс на котором он учится, группа, средний балл по всем изучаемым дисциплинам и т.д. Таким образом, исходя из этих данных, можно выделить следующие важные для нас сущности — преподаватель и студент. Абстрактный класс  в этом случае — человек, то есть этот класс будет чем-то общим между студентом и преподавателем. Теперь попробуем реализовать нашу задачу в коде C#.

Абстрактный класс

Создадим наш первый абстрактный класс, который будет представлять в программе какого-либо человека:

abstract class Person
{
    public string Name { get; set; }
    public string Family { get; set; }

    public Person(string name, string family)
    {
        Name = name;
        Family = family;
    }

    public string Display()
    {
        return $"{Family} {Name}";
    }
}

У класса определено два свойства: имя (Name) и фамилия (Family), а также конструктор и метод Diasplay, возвращающий строку, содержащую фамилию и имя человека. Ключевое слово abstract говорит нам о том, что класс является абстрактным и мы не можем воспользоваться конструктором абстрактного класса напрямую (создать объект абстрактного класса). Например, вот такой вызов

Person person = new Person("Вася", "Пупкин");

приведет к ошибке

Ошибка CS0144 Не удается создать экземпляр абстрактного типа или интерфейса «Person»

Теперь используем ключевую возможность ООП — наследование и создадим производный от Person класс студента.

class Student: Person
{
    string Group { get; set; }
    
    public Student(string name, string family, string group) : base(name, family)
    {
        Group = group;
    }
}

Здесь мы уже определили свой конструктор для класса и добавили для класса свое свойство — Group (название группы, в которой учится студент). Теперь, мы можем создать экземпляр этого класса и, например, воспользоваться методом Diasplay:

Person student = new Student("Вася", "Пупкин", "ГВН-105");
Student student1 = new Student("Ваня", "Иванов", "ГВН-105");
Console.WriteLine(student.Display());
Console.WriteLine(student1.Display());

Обратите внимание, что в первой строке мы объявили переменную student как Person (наш абстрактный класс), однако создали объект класса-потомка (Student) и по факту в нашей переменной лежит именно объект класса Student. Во втором случае мы использовали уже класс-потомок для описания типа переменной. Теперь, в классе Student переопределим метод Display следующим образом:

public string Display(bool withGroup)
{
    if (withGroup)
        return string.Concat(new string[] { $"{Group} ", Display() });
    else
        return Display();
}

то есть, в зависимости от параметра withGroup мы будем выводить либо просто имя и фамилию студента, либо перед именем и фамилией ставить группу. В коде ниже, оба вызова методов будут выполнены:

Console.WriteLine(student.Display()); //метод из абстрактного класса
Console.WriteLine(student1.Display(true));

Соответственно, класс преподавателя можно сделать вот таким:

class Teacher: Person
{
    public int Seniority { get; set; }
    
    public Teacher(string name, string family, int seniority) : base(name, family)
    {
        Seniority = seniority;
    }
}

Опять же, несмотря на то, что классы Student и Teacher — это вполне самостоятельные классы, описывающие разные сущности, в программе мы можем объявить их, используя абстрактный класс Person.

Использование методов наследника и предка

Используя наши разработанные классы, попробуйте сделать вот такой вызов метода Display():

Person student = new Student("Вася", "Пупкин", "ГВН-105");
Console.WriteLine(student.Display(true));

Несмотря на то, что метод Diaplay() у класса Student переопределен, мы получим следующую ошибку:

Ошибка CS1501 Ни одна из перегрузок метода «Display» не принимает 1 аргументов.

Это произошло по тому, что для определения переменной student мы использовали класс Person у которого действительно нет метода Diaplay, который бы принимал хотя бы один аргумент. Чтобы воспользоваться методом наследника мы должны указать компилятору, что у нас лежит по факту в переменной student:

string res = ((Student)student).Display(true);
Console.WriteLine(res);

строку, возвращаемую методом Display я для наглядности вынес отдельной строкой в коде. Перед переменной мы указали в скобках тип данных (Student). Если же мы хотим воспользоваться методом Display у предка, то здесь ничего указывать не требуется:

string res = student.Display();
Console.WriteLine(res);

Абстрактные члены класса

Абстрактные классы в C# моuen также иметь абстрактные члены классов, которые, также как и класс, определяются с помощью ключевого слова abstract и, при этом, не имеют никакого функционала. Абстрактными могут быть:

При использовании абстрактных членов класса следует иметь в виду следующее:

  1. абстрактные члены классов не должны иметь модификатор private.
  2. производный класс обязан переопределить и реализовать все абстрактные методы и свойства, которые имеются в базовом абстрактном классе. При переопределении в производном классе такой метод или свойство объявляются с модификатором override.
  3. если класс имеет хотя бы один абстрактный член, то этот класс должен быть определен как абстрактный.

В качестве примера, сделаем в классе Person абстрактным метод Display. Как только вы пометите метод ключевым словом abstract, компилятор сразу сообщит вам следующее:

Ошибка CS0500 «Person.Display()» не может объявить тело, потому что помечен как abstract.

так как, как было сказано выше, абстрактные члены класса не должны иметь никакой функциональности, то есть абстрактный метод в классе должен выглядеть следующим образом:

abstract class Person
 {
     abstract public string Display();
 }

Теперь для наших классов Student и Teacher появится сообщение от C#:

Ошибка CS0534 «Student» не реализует наследуемый абстрактный член «Person.Display()».

Переопределим и реализуем абстрактный метод в наших классах:

class Student: Person
{
    public override string Display()
    {
        return $"{Group} {Name} {Family}";
    }
}
class Teacher: Person
{
    public override string Display()
    {
        return $"{Name} {Family} Стаж: {Seniority}";
    }
}

теперь метод Display() переопределен (не путать с перегружен) и можно собрать нашу программу и проверить её работоспособность.

Отказ от реализации абстрактных членов

Несмотря на то, что в производных классах мы обязаны переопределить все абстрактные свойства и методы, мы можем этого не делать, но, при этом, производный класс также должен быть абстрактным. Например,

abstract class Anybody : Person
{ 
  //для такого класса переопределять метод Display() не требуется
}

Итого

В C# мы можем создавать некие общие для набора сущностей классы, которые называются абстрактными. Абстрактные классы помечаются ключевым словом abstract и объекты такого класса не могут быть созданы с использованием конструктора абстрактного класса. В абстрактном классе могут быть абстрактные члены (методы, свойства, индексаторы, события). При этом, производный от абстрактного класс должен переопределять все абстрактные члены, если производный класс не является также абстрактным.

уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.
Подписаться
Уведомить о
guest
0 Комментарий
Межтекстовые Отзывы
Посмотреть все комментарии