Полиморфизм
Рассмотрим семейство классов A1, A2, ... An, связанных отношением наследования. Класс Ak+1 является прямым потомком класса Ak. Пусть создана последовательность объектов x1, x2, ... xn, где xk - объект класса Ak. Пусть в классе A1 создан метод M с модификатором virtual, переопределяемый всеми потомками, так что в рамках семейства классов метод M существует в n формах, каждая из которых задает реализацию метода, выбранную соответствующим потомком. Рассмотрим основную операцию, инициирующую объектные вычисления - вызов объектом метода класса:
x1.M(arg1, arg2, … argN)
Контролем типов называется проверка каждого вызова, удостоверяющая, что:
в классе A1 объекта x1 действительно имеется метод M ;
у метода М действительно N формальных аргументов;
список фактических аргументов в точке вызова соответствует по числу и типам списку формальных аргументов метода M, заданного в классе A1.
Язык C#, как и большинство других языков программирования, позволяет выполнить эту проверку еще на этапе компиляции и в случае нарушений выдать сообщение об ошибке. Контроль типов, выполняемый на этапе компиляции, называется статическим контролем типов. Языки программирования, называемые динамическими, например, язык Smalltalk, производят этот контроль динамически - в ходе работы программы непосредственно перед выполнением метода. Ошибки, обнаруживаемые при динамическом контроле типов, трудно исправимы. У динамических языков есть свои преимущества - прежде всего, простота.
Перейдем к рассмотрению связывания. Снова рассмотрим основную операцию - вызов
x1.M(arg1, arg2, … argN);
Предположим, что статический контроль типов для этого вызова успешно выполнен. Рассмотрим еще один аспект, связанный с этим вызовом. x1 - это ссылка, которая связана с объектом, расположенным в динамической памяти. Тип объекта и тип ссылки могут не совпадать. Другими словами, объект, созданный в динамической памяти, может не принадлежать классу A1.
Предположим, что вызову метода M объектом x1 предшествует присваивание
x1 = y;
Это ссылочное присваивание, поскольку x1 - ссылка. Присваивание является допустимым, если объект y принадлежит классу, являющемуся потомком класса объекта x1. Таким образом, в точке вызова ссылка x1 может быть связана с объектом любого класса нашего семейства A1, …AN. В каждом из этих классов существует метод M с одной и той же сигнатурой. Метод M полиморфен: имея одно и то же имя и сигнатуру, он существует в разных формах - для каждого класса задана собственная реализация метода. Возникает естественный вопрос, какой же метод M следует вызывать - метод класса A1 (метод ссылки) или метод того класса, которому принадлежит реальный объект в динамической памяти (метод объекта). Возможны два решения этого вопроса, и оба варианта используются на практике.
Статическим связыванием называется связывание цели вызова и вызываемого метода на этапе компиляции, когда с сущностью связывается метод класса, заданного при объявлении сущности - метод ссылки.
Динамическим связыванием называется связывание цели вызова и вызываемого метода на этапе выполнения, когда с сущностью связывается метод класса объекта, связанного с сущностью в момент выполнения - метод объекта.
При статическом связывании метод выбирается из класса сущности, при динамическом - из класса объекта, связанного с сущностью. Понятно, что на этапе компиляции возможно только статическое связывание, поскольку только в период выполнения можно определить, с объектом какого класса связана данная сущность. Это может быть класс любого из потомков класса сущности.
Статическое связывание более эффективно в реализации, поскольку может быть сделано на этапе компиляции, так что при выполнении не потребуется никаких проверок. Динамическое связывание требует накладных расходов в период выполнения.
В языке C# принята следующая стратегия связывания. По умолчанию предполагается статическое связывание. Для того чтобы выполнялось динамическое связывание, родительский класс, впервые создающий метод, должен снабдить метод модификатором virtual или abstract, в классах потомках такой виртуальный метод будет иметь модификатор override.
Под полиморфизмом в ООП понимают способность одного и того же программного текста
x1.M(arg1, arg2, … argN);
выполняться по-разному, в зависимости от того, с каким объектом связана сущность x. Полиморфизм гарантирует, что вызываемый метод M будет принадлежать классу объекта, связанному с сущностью x. В основе полиморфизма, характерного для семейства классов, лежат три механизма.
Одностороннее присваивание объектов внутри семейства классов. Сущность, базовым классом которой является класс предка, можно связать с объектом любого из потомков. Другими словами, для введенной нами последовательности объектов xk присваивание xi = xj допустимо для всех j >=i.
Переопределение потомком метода, наследованного от родителя. Благодаря переопределению в семействе классов существует совокупность полиморфных методов с одинаковым именем и одинаковой сигнатурой, но разной реализацией.
Динамическое связывание, позволяющее в момент выполнения вызывать метод, принадлежащий объекту, с которым связана сущность в момент вызова.
В совокупности это и называется полиморфизмом семейства классов. Целевую сущность часто называют полиморфной сущностью, вызываемый метод - полиморфным методом, сам вызов - полиморфным вызовом.
Рассмотрим пример:
class Ferst
{
public virtual void P()
{
Console.WriteLine("Ferst");
}
}
class Second : Ferst
{
public override void P()
{
Console.WriteLine("Second");
}
}
class Third : Second
{
public override void P()
{
Console.WriteLine("Third");
}
}
class Program
{
static void Main()
{
Ferst member1 = new Ferst();
member1.P();
Second member2 = new Second();
member2.P();
Third member3 = new Third();
member3.P();
member2 = member3;
member2.P();
member1 = member3;
member1.P();
Console.ReadLine();
}
}
В приведенном примере создано три класса. Класс Ferst является родителем для класса Second, который в свою очередь является родителем для класса Third. В каждом классе определен метод P(). Причем в классе Ferst он указан как virtual. В оставшихся двух классах данный метод переопределен с использованием ключевого слова override. В зависимости от класса при выполнении метода P() на консоль выводиться различная информация.
В точке вызова программы создается по одному экземпляру для каждого из описанных классов. После создании каждого объекта выполняется вызов метода P(). Затем для демонстрации принципов полиморфизма объект member2 класса Second используется в качестве ссылки на объект member3 класса Third. При выполнении метода P() определенного на объекте member2 выполняется метод определенный в классе Third. Аналогично для объекта member1.
- Основные понятия ооп
- Модификаторы доступа
- Uml, назначение, типы диаграм.
- Диаграмма классов
- Деструкторы
- Свойства
- Индексаторы
- Этапы проектирования класса.
- Отношения между классами
- Вложенные классы
- Наследование
- Отношения и их графическое изображение на диаграмме классов
- Полиморфизм
- Абстрактные классы
- Классы без потомков
- Интерфейсы
- Изображение интерфейсов и абстрактных классов на диаграмме классов
- Вложенные классы
- Статические классы
- Делегаты
- Криптография и .Net
- Сетевое программирование в c#
- Создание простого клиент-серверного приложения используя сокеты
- Многопоточность
- Разработка библиотек dll