Главная страница
Навигация по странице:

  • Ключевое слово base.

  • Конструкторы в производных классах.

  • Порядок вызова конструкторов

  • Лекция4. Наследование. Тема Наследование


    Скачать 0.58 Mb.
    НазваниеТема Наследование
    Дата14.03.2022
    Размер0.58 Mb.
    Формат файлаpdf
    Имя файлаЛекция4. Наследование.pdf
    ТипДокументы
    #396531

    Тема 4. Наследование.
    Наследование (inheritance) является одним из ключевых моментов ООП.
    Благодаря наследованию один класс может унаследовать функциональность другого класса.
    Пусть у нас есть следующий класс Person, который описывает отдельного человека: class Person
    { private string _name; public string Name
    { get { return _name; } set { _name = value; }
    } public void Display()
    {
    Console.WriteLine(Name);
    }
    }
    Но вдруг нам потребовался класс, описывающий сотрудника предприятия – класс
    Employee. Поскольку этот класс будет реализовывать тот же функционал, что и класс
    Person, так как сотрудник – это также и человек, то было бы рационально сделать класс
    Employee производным (или наследником, или подклассом) от класса Person, который, в свою очередь, называется базовым классом или родителем (или суперклассом): class Employee : Person
    {
    }
    После двоеточия мы указываем базовый класс для данного класса. Для класса
    Employee базовым является Person, и поэтому класс Employee наследует все те же свойства, методы, поля, которые есть в классе Person. Единственное, что не передается при наследовании, это конструкторы базового класса.
    Таким образом, наследование реализует отношение is-a (является), объект класса
    Employee также является объектом класса Person: static void Main(string[] args)
    {
    Person p = new Person { Name = "Tom"}; p.Display(); p = new Employee { Name = "Sam" }; p.Display();
    Console.Read();
    }
    И поскольку объект Employee является также и объектом Person, то мы можем так определить переменную: Person p = new Employee().

    По умолчанию все классы наследуются от базового класса Object, даже если мы явным образом не устанавливаем наследование. Поэтому выше определенные классы
    Person и Employee кроме своих собственных методов, также будут иметь и методы класса
    Object: ToString(), Equals(), GetHashCode() и GetType().
    Все классы по умолчанию могут наследоваться. Однако здесь есть ряд ограничений.
    Не поддерживается множественное наследование, класс может наследоваться только от одного класса.
    При создании производного класса надо учитывать тип доступа к базовому классу - тип доступа к производному классу должен быть таким же, как и у базового класса, или более строгим. То есть, если базовый класс у нас имеет тип доступа internal, то производный класс может иметь тип доступа internal или private, но не public.
    Однако следует также учитывать, что если базовый и производный класс находятся в разных сборках (проектах), то в этом случае производный класс может наследовать только от класса, который имеет модификатор public.
    Если класс объявлен с модификатором sealed, то от этого класса нельзя наследовать и создавать производные классы. Например, следующий класс не допускает создание наследников: sealed class Admin
    {
    }
    Нельзя унаследовать класс от статического класса.
    Доступ к членам базового класса из класса-наследника
    Вернемся к нашим классам Person и Employee. Хотя Employee наследует весь функционал от класса Person, посмотрим, что будет в следующем случае: class Employee : Person
    { public void Display()
    {
    Console.WriteLine(_name);
    }
    }
    Этот код не сработает и выдаст ошибку, так как переменная _name объявлена с модификатором private и поэтому к ней доступ имеет только класс Person. Но зато в классе Person определено общедоступное свойство Name, которое мы можем использовать, поэтому следующий код у нас будет работать нормально: class Employee : Person
    { public void Display()
    {
    Console.WriteLine(Name);
    }
    }
    Таким образом, производный класс может иметь доступ только к тем членам базового класса, которые определены с модификаторами private protected (если
    базовый и производный класс находятся в одной сборке), public, internal (если базовый и производный класс находятся в одной сборке), protected и protected internal.
    Ключевое слово base. Теперь добавим в наши классы конструкторы (рисунок 4.1).
    Класс Person имеет конструктор, который устанавливает свойство Name. Поскольку класс Employee наследует и устанавливает то же свойство Name, то логично было бы не писать по сто раз код установки, а как-то вызвать соответствующий код класса Person. К тому же свойств, которые надо установить в конструкторе базового класса, и параметров может быть гораздо больше.
    С помощью ключевого слова base мы можем обратиться к базовому классу. В нашем случае в конструкторе класса Employee нам надо установить имя и компанию. Но имя мы передаем на установку в конструктор базового класса, то есть в конструктор класса
    Person, с помощью выражения base(name).
    Рисунок 4.1 – Наследование
    Консольный вывод:
    Ссылка на программу в онлайн-компиляторе repl.it:
    https://repl.it/repls/FloralwhiteWorriedCertification

    Конструкторы в производных классах. Конструкторы не передаются производному классу при наследовании. И если в базовом классе не определен конструктор по умолчанию без параметров, а только конструкторы с параметрами (как в случае с базовым классом Person), то в производном классе мы обязательно должны вызвать один из этих конструкторов через ключевое слово base. Например, из класса Employee уберем определение конструктора: class Employee : Person
    { public string Company { get; set; }
    }
    В данном случае мы получим ошибку, так как класс Employee не соответствует классу Person, а именно не вызывает конструктор базового класса. Даже если бы мы добавили какой-нибудь конструктор, который бы устанавливал все те же свойства, то мы все равно бы получили ошибку: public Employee(string name, string company)
    {
    Name = name;
    Company = company;
    }
    То есть в классе Employee через ключевое слово base надо явным образом вызвать конструктор класса Person: public Employee(string name, string company) : base(name)
    {
    Company = company;
    }
    Либо в качестве альтернативы мы могли бы определить в базовом классе конструктор без параметров: class Person
    {
    // остальной код класса
    // конструктор по умолчанию public Person()
    {
    FirstName = "Tom";
    Console.WriteLine("Вызов конструктора без параметров");
    }
    }
    Тогда в любом конструкторе производного класса, где нет обращения конструктору базового класса, все равно неявно вызывался бы этот конструктор по умолчанию.
    Например, следующий конструктор: public Employee(string company)
    {

    Company = company;
    }
    Фактически был бы эквивалентен следующему конструктору: public Employee(string company) : base()
    {
    Company = company;
    }
    Порядок вызова конструкторов. При вызове конструктора класса сначала отрабатывают конструкторы базовых классов и только затем конструкторы производных.
    Например, возьмем следующие классы (рисунок 4.2).
    Рисунок 4.2 – Порядок вызова конструкторов
    Мы получим следующий консольный вывод:
    В итоге мы получаем следующую цепь выполнений.

    Вначале вызывается конструктор Employee(string name, int age, string company). Он делегирует выполнение конструктору Person(string name, int age).
    Вызывается конструктор Person(string name, int age), который сам пока не выполняется и передает выполнение конструктору Person(string name).
    Вызывается конструктор Person(string name), который передает выполнение конструктору класса System.Object, так как это базовый по умолчанию класс для
    Person.
    Выполняется конструктор System.Object.Object(), затем выполнение возвращается конструктору Person(string name).
    Выполняется тело конструктора Person(string name), затем выполнение возвращается конструктору Person(string name, int age).
    Выполняется тело конструктора Person(string name, int age), затем выполнение возвращается конструктору Employee(string name, int age, string company).
    Выполняется тело конструктора Employee(string name, int age, string company). В итоге создается объект Employee.
    Ссылка на программу в онлайн-компиляторе repl.it
    :
    https://repl.it/repls/SelfishTrustyElectricity.


    написать администратору сайта