Лекция 3
Основы наследования
Основы наследования
Основы наследования
Class Object
Ограничения
Ключевое слово base
Порядок вызова конструкторов
Порядок вызова конструкторов
Наследование полей и методов
Совместимость типов при наследовании
Переопределение метода (позднее связывание)
Переопределение метода (позднее связывание)
Сокрытие (Раннее связывание)
Полиморфизм
Полиморфизм
Инкапсуляция
88.91K
Категория: ПрограммированиеПрограммирование

Основы наследования. Лекция 3

1. Лекция 3

Основы наследования

2. Основы наследования

Наследование является одним из трех основополагающих принципов
объектно-ориентированного программирования, поскольку оно
допускает создание иерархических классификаций.
Благодаря наследованию можно создать общий класс, в котором
определяются характерные особенности, присущие множеству
связанных элементов. От этого класса могут затем наследовать другие,
более конкретные классы, добавляя в него свои индивидуальные
особенности.

3. Основы наследования

В языке C# класс, который наследуется, называется базовым, а класс,
который наследует, — производным. Следовательно, производный
класс представляет собой специализированный вариант базового
класса. Он наследует все переменные, методы, свойства и
индексаторы, определяемые в базовом классе, добавляя к ним свои
собственные элементы.
Поддержка наследования в C# состоит в том, что в объявление одного
класса разрешается вводить другой класс. Для этого при объявлении
производного класса указывается базовый класс. При установке
между классами отношения "является" строится зависимость между
двумя или более типами классов. Базовая идея, лежащая в основе
классического наследования, заключается в том, что новые классы
могут создаваться с использованием существующих классов в
качестве отправной точки

4. Основы наследования

Для любого производного класса можно указать только один базовый
класс. В C# не предусмотрено наследование нескольких базовых классов
в одном производном классе. (В этом отношении C# отличается от С++,
где допускается наследование нескольких базовых классов. Данное
обстоятельство следует принимать во внимание при переносе кода С++ в
C#.) Тем не менее можно создать иерархию наследования, в которой
производный класс становится базовым для другого производного класса.
(Разумеется, ни один из классов не может быть базовым для самого себя
как непосредственно, так и косвенно.) Но в любом случае производный
класс наследует все члены своего базового класса, в том числе
переменные экземпляра, методы, свойства.
Главное преимущество наследования заключается в следующем: как
только будет создан базовый класс, в котором определены общие для
множества объектов атрибуты, он может быть использован для создания
любого числа более конкретных производных классов. А в каждом
производном классе может быть точно выстроена своя собственная
классификация.

5. Class Object

По умолчанию все классы наследуются от базового класса Object,
даже если мы явным образом не устанавливаем наследование.
Поэтому выше определенные классы Person и Employee кроме своих
собственных методов, также будут иметь и методы класса Object:
ToString(), Equals(), GetHashCode() и GetType().

6. Ограничения

Все классы по умолчанию могут наследоваться. Однако здесь есть ряд
ограничений:
Не поддерживается множественное наследование, класс может наследоваться
только от одного класса.
При создании производного класса надо учитывать тип доступа к базовому классу тип доступа к производному классу должен быть таким же, как и у базового класса,
или более строгим. То есть, если базовый класс у нас имеет тип доступа internal, то
производный класс может иметь тип доступа internal или private, но не public.
Однако следует также учитывать, что если базовый и производный класс находятся в
разных сборках (проектах), то в этом случае производный класс может наследовать
только от класса, который имеет модификатор public.
Если класс объявлен с модификатором sealed, то от этого класса нельзя
наследовать и создавать производные классы. Например, следующий класс не
допускает создание наследников

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

Класс Person имеет конструктор, который устанавливает свойство
Name. Поскольку класс Employee наследует и устанавливает то же
свойство Name, то логично было бы не писать по сто раз код установки,
а как-то вызвать соответствующий код класса Person. К тому же свойств,
которые надо установить в конструкторе базового класса, и
параметров может быть гораздо больше.
С помощью ключевого слова base мы можем обратиться к базовому
классу. В нашем случае в конструкторе класса Employee нам надо
установить имя и должность. Но имя мы передаем на установку в
конструктор базового класса, то есть в конструктор класса Person, с
помощью выражения base(name).
Конструкторы не передаются производному классу при наследовании.
И если в базовом классе не определен конструктор по умолчанию без
параметров, а только конструкторы с параметрами (как в случае с
базовым классом Person), то в производном классе мы обязательно
должны вызвать один из этих конструкторов через ключевое слово base.

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

Если в конструкторе производного класса явный вызов
конструктора базового класса отсутствует, автоматически
вызывается конструктор базового класса без параметров.
Для иерархии, состоящей из нескольких уровней, конструкторы
базовых классов вызываются, начиная с самого верхнего уровня.
После этого выполняются конструкторы тех элементов класса,
которые являются объектами, в порядке их объявления в классе,
а затем исполняется конструктор класса.
Если конструктор базового класса требует указания
параметров, он должен быть вызван явным образом в
конструкторе производного класса в списке инициализации.

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

1. Вначале вызывается конструктор Employee(string name, int age, Position
position). Он делегирует выполнение конструктору Person(string name, int
age)
2. Вызывается конструктор Person(string name, int age), который сам пока не
выполняется и передает выполнение конструктору Person(string name)
3. Вызывается конструктор Person(string name), который передает выполнение
конструктору класса System.Object, так как это базовый по умолчанию
класс для Person.
4. Выполняется конструктор System.Object.Object(), затем выполнение
возвращается конструктору Person(string name)
5. Выполняется тело конструктора Person(string name), затем выполнение
возвращается конструктору Person(string name, int age)
6. Выполняется тело конструктора Person(string name, int age), затем
выполнение возвращается конструктору Employee(string name, int age,
Position position)
7. Выполняется тело конструктора Employee(string name, int age, string
company). В итоге создается объект Employee

10. Наследование полей и методов

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

11. Совместимость типов при наследовании

Объекту базового класса можно присвоить объект производного
класса:
предок
потомок
Это делается для единообразной работы со всей иерархией.
При преобразовании программы из исходного кода в
исполняемый используется два механизма связывания:
раннее – early binding – до выполнения программы
позднее (динамическое) – late binding – во время выполнения

12. Переопределение метода (позднее связывание)

Происходит на этапе выполнения программы
Признак – ключевое слово virtual в базовом классе:
virtual public void GetInfo() ...
Компилятор формирует для virtual методов таблицу виртуальных методов. В
нее записываются адреса виртуальных методов (в том числе
унаследованных) в порядке описания в классе.
Для каждого класса создается одна таблица.
Связь с таблицей устанавливается при создании объекта с помощью кода,
автоматически помещаемого компилятором в конструктор объекта.
Если в производном классе требуется переопределить виртуальный метод,
используется ключевое слово override:
override public void GetInfo() ...
Переопределенный виртуальный метод должен обладать таким же
набором параметров, как и одноименный метод базового класса.

13. Переопределение метода (позднее связывание)

методы класса С
VMT для С
адрес Метод 1
Метод 1
Метод 2 ..
Виртуальные
методы T
Метод 1
адрес Метод 2
Метод 2

14. Сокрытие (Раннее связывание)

Ссылки разрешаются до выполнения программы
Поэтому компилятор может руководствоваться только типом
переменной, для которой вызывается метод или свойство. То, что в этой
переменной в разные моменты времени могут находиться ссылки на
объекты разных типов, компилятор учесть не может.
Поэтому для ссылки базового типа, которой присвоен объект
производного типа, можно вызвать только методы и свойства,
определенные в базовом классе (т.е. возможность доступа к
элементам класса определяется типом ссылки, а не типом объекта,
на который она указывает).

15. Полиморфизм

Виртуальные методы базового класса определяют интерфейс всей иерархии.
Интерфейс может расширяться в потомках за счет добавления новых
виртуальных методов (нежелательно).
Переопределять виртуальный метод в каждом из потомков не обязательно: если
он выполняет устраивающие потомка действия, метод наследуется.
Механизм вызова виртуального метода:
из объекта берется адрес таблицы вирт. методов соотв. класса
из нее выбирается адрес метода
ему передается управление.
Итак, при использовании виртуальных методов из всех одноименных методов
иерархии всегда выбирается тот, который соответствует фактическому типу
вызвавшего его объекта.
Виртуальные методы — основное проявление полиморфизма.

16. Полиморфизм

При описании классов рекомендуется определять в качестве
виртуальных те методы, которые в производных классах должны
реализовываться по-другому.
Если во всех классах иерархии метод будет выполняться одинаково,
его лучше определить как обычный метод.

17. Инкапсуляция

Инкапсуляция – это скрытие реализации объекта от конечного
пользователя, которое в Си-шарп осуществляется при помощи
модификаторов доступа (private, public…). Конечным пользователем
объекта здесь выступает либо объект наследник, либо программист.
English     Русский Правила