Слайд 2Основы наследования
Наследование является одним из трех основополагающих принципов объектно-ориентированного программирования,
поскольку оно допускает создание иерархических классификаций.
Благодаря наследованию можно создать
общий класс, в котором определяются характерные особенности, присущие множеству связанных элементов. От этого класса могут затем наследовать другие, более конкретные классы, добавляя в него свои индивидуальные особенности.
Слайд 3Основы наследования
В языке C# класс, который наследуется, называется базовым, а класс,
который наследует, — производным. Следовательно, производный класс представляет собой специализированный вариант
базового класса. Он наследует все переменные, методы, свойства и индексаторы, определяемые в базовом классе, добавляя к ним свои собственные элементы.
Поддержка наследования в C# состоит в том, что в объявление одного класса разрешается вводить другой класс. Для этого при объявлении производного класса указывается базовый класс. При установке между классами отношения "является" строится зависимость между двумя или более типами классов. Базовая идея, лежащая в основе классического наследования, заключается в том, что новые классы могут создаваться с использованием существующих классов в качестве отправной точки
Слайд 4Основы наследования
Для любого производного класса можно указать только один базовый
класс. В C# не предусмотрено наследование нескольких базовых классов в
одном производном классе. (В этом отношении C# отличается от С++, где допускается наследование нескольких базовых классов. Данное обстоятельство следует принимать во внимание при переносе кода С++ в C#.) Тем не менее можно создать иерархию наследования, в которой производный класс становится базовым для другого производного класса. (Разумеется, ни один из классов не может быть базовым для самого себя как непосредственно, так и косвенно.) Но в любом случае производный класс наследует все члены своего базового класса, в том числе переменные экземпляра, методы, свойства.
Главное преимущество наследования заключается в следующем: как только будет создан базовый класс, в котором определены общие для множества объектов атрибуты, он может быть использован для создания любого числа более конкретных производных классов. А в каждом производном классе может быть точно выстроена своя собственная классификация.
Слайд 5Class 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Порядок вызова конструкторов
Вначале вызывается конструктор Employee(string name, int age, Position
position). Он делегирует выполнение конструктору 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, Position position)
Выполняется тело конструктора 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Переопределение метода (позднее связывание)
методы класса С
Метод 2
Метод 1
Метод 2
..
Метод 1
адрес Метод 2
адрес Метод 1
VMT для С
Виртуальные методы T
Слайд 14Сокрытие (Раннее связывание)
Ссылки разрешаются до выполнения программы
Поэтому компилятор может руководствоваться
только типом переменной, для которой вызывается метод или свойство. То,
что в этой переменной в разные моменты времени могут находиться ссылки на объекты разных типов, компилятор учесть не может.
Поэтому для ссылки базового типа, которой присвоен объект производного типа, можно вызвать только методы и свойства, определенные в базовом классе (т.е. возможность доступа к элементам класса определяется типом ссылки, а не типом объекта, на который она указывает).
Слайд 15Полиморфизм
Виртуальные методы базового класса определяют интерфейс всей иерархии.
Интерфейс может
расширяться в потомках за счет добавления новых виртуальных методов (нежелательно).
Переопределять виртуальный метод в каждом из потомков не обязательно: если он выполняет устраивающие потомка действия, метод наследуется.
Механизм вызова виртуального метода:
из объекта берется адрес таблицы вирт. методов соотв. класса
из нее выбирается адрес метода
ему передается управление.
Итак, при использовании виртуальных методов из всех одноименных методов иерархии всегда выбирается тот, который соответствует фактическому типу вызвавшего его объекта.
Виртуальные методы — основное проявление полиморфизма.
Слайд 16Полиморфизм
При описании классов рекомендуется определять в качестве виртуальных те методы,
которые в производных классах должны реализовываться по-другому.
Если во всех
классах иерархии метод будет выполняться одинаково, его лучше определить как обычный метод.
Слайд 17Инкапсуляция
Инкапсуляция – это скрытие реализации объекта от конечного пользователя, которое
в Си-шарп осуществляется при помощи модификаторов доступа (private, public…). Конечным
пользователем объекта здесь выступает либо объект наследник, либо программист.