Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
C#, 320 стр..doc
Скачиваний:
8
Добавлен:
16.11.2018
Размер:
3.64 Mб
Скачать

Три механизма, обеспечивающие полиморфизм

Под полиморфизмом в ООП понимают способность одного и того же программного текста x.M выполняться по-разному, в зависимости от того, с каким объектом связана сущность x. Полиморфизм гарантирует, что вызываемый метод M будет принадлежать классу объекта, связанному с сущностью x. В основе полиморфизма, характерного для семейства классов, лежат три механизма:

  • одностороннее присваивание объектов внутри семейства классов; сущность, базовым классом которой является класс предка, можно связать с объектом любого из потомков. Другими словами, для введенной нами последовательности объектов xk присваивание xi = xj допустимо для всех j >=i;

  • переопределение потомком метода, наследованного от родителя. Благодаря переопределению, в семействе классов существует совокупность полиморфных методов с одним именем и сигнатурой;

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

В совокупности это и называется полиморфизмом семейства классов. Целевую сущность часто называют полиморфной сущностью, вызываемый метод - полиморфным методом, сам вызов - полиморфным вызовом.

Вернемся к нашему примеру с классами Found, Derived, ChildDerived. Напомню, в родительском классе определен виртуальный метод VirtMethod и переопределен виртуальный метод ToString родительского класса object. Потомок класса Found - класс Derived переопределяет эти методы:

public override void VirtMethod()

{

Console.WriteLine("Сын: " + this.ToString());

}

public override string ToString()

{

return(String.Format("поля: name = {0},

credit = {1},debet ={2}",name, credit, debet));

}

Потомок класса Derived - класс ChildDerived не создает новых полей. Поэтому он использует во многом методы родителя. Его конструктор состоит из вызова конструктора родителя:

public ChildDerived(string name, int cred, int deb):base (name,cred, deb)

{}

Нет и переопределения метода Tostring, поскольку используется реализация родителя. А вот метод VirtMethod переопределяется:

public override void VirtMethod()

{

Console.WriteLine("внук: " + this.ToString());

}

В классе Found определены два невиртуальных метода NonVirtmethod и Work, наследуемые потомками Derived и ChildDerived без всяких переопределений. Вы ошибаетесь, если думаете, что работа этих методов полностью определяется базовым классом Found. Полиморфизм делает их работу куда более интересной. Давайте рассмотрим в деталях работу метода Work:

public void Work()

{

VirtMethod();

NonVirtMethod();

Analysis();

}

При компиляции метода Work будет обнаружено, что вызываемый метод VirtMethod является виртуальным, поэтому для него будет применяться динамическое связывание. Это означает, что вопрос о вызове метода откладывается до момента, когда метод Work будет вызван объектом, связанным с x. Объект может принадлежать как классу Found, так и классам Derived и ChildDerived, в зависимости от класса объекта и будет вызван метод этого класса.

Для не виртуальных методов NonVirtMethod и Analysis будет применено статическое связывание, так что Work всегда будет вызывать методы, принадлежащие классу Found. Однако и здесь не все просто. Метод NonVirtMethod

public void NonVirtMethod()

{

Console.WriteLine ("Мать: "+ this.ToString());

}

в процессе своей работы вызывает виртуальный метод ToString. Опять-таки, для метода ToString будет применяться динамическое связывание, и в момент выполнения будет вызываться метод класса объекта.

Что же касается метода Analysis, определенного в каждом классе, то всегда в процессе работы Work будет вызываться только родительский метод анализа из-за стратегии статического связывания.

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

Класс Found, создающий метод Work, говорит примерно следующее: "Я предоставляю этот метод своим потомкам. Потомок, вызвавший этот метод, должен иметь VirtMethod, выполняющий специфическую для потомка часть работы; конечно, потомок может воспользоваться и моей реализацией, но допустима и его собственная реализация. Затем часть работы выполняю я сам, но выдача информации об объекте определяется самим объектом. Заключительную часть работы, связанную с анализом, я потомкам не доверяю и делаю ее сам".

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]