Статическим связыванием называется связывание цели вызова и вызываемого метода на этапе компиляции, когда с сущностью связывается метод класса, заданного при объявлении сущности.
Динамическим связыванием называется связывание цели вызова и вызываемого метода на этапе выполнения, когда с сущностью связывается метод класса объекта, связанного с сущностью в момент выполнения.
При статическом связывании метод выбирается из класса сущности, при динамическом — из класса объекта, связанного с сущностью. Понятно, что на этапе компиляции возможно только статическое связывание, поскольку только в период выполнения можно определить, с объектом какого класса связана данная сущность. Это может быть класс любого из потомков класса сущности.
Какой же из видов связывания следует применять? Статическое связывание более эффективно в реализации, поскольку может быть сделано на этапе компиляции, так что при выполнении не потребуется никаких проверок. Динамическое связывание требует накладных расходов в период выполнения. Однако во многих случаях преимущества динамического связывания столь значительны, что о затратах не стоит и беспокоиться.
Уже достаточно давно разработан эффективный механизм реализации динамического связывания. Еще на этапе компиляции подготавливается так называемая таблица виртуальных методов, содержащая их адреса. Связывание объекта хk с принадлежащим ему методом Mk производится выбором соответствующего элемента из этой таблицы и выполняется ненамного сложнее, чем получение по индексу соответствующего элемента массива.
В языке C# принята следующая стратегия связывания. По умолчанию предполагается статическое связывание. Для того чтобы выполнялось динамическое связывание, метод родительского класса должен снабжаться модификатором virtual или abstract, а его потомки должны иметь модификатор override.
Три механизма, обеспечивающие полиморфизм
Под полиморфизмом в ООП понимают способность одного и того же программного текста х. M выполняться по-разному, в зависимости оттого, с каким объектом связана сущность х. Полиморфизм гарантирует, что вызываемый метод М будет принадлежать классу объекта, связанному с сущностью х.
В основе полиморфизма, характерного для семейства классов, лежат три механизма:
• одностороннее присваивание объектов внутри семейства классов; сущность, базовым классом которой является класс предка, можно связать с объектом любого из потомков. Другими словами, для введенной нами последовательности объектов хk присваивание xi = xj допустимо для всех j >=i;
• переопределение потомком метода, наследованного от родителя. Благодаря переопределению, в семействе классов существует совокупность полиморфных методов с одним именем и сигнатурой;
• динамическое связывание, позволяющее в момент выполнения вызывать метод, который принадлежит целевому объекту.
В совокупности это и называется полиморфизмом семейства классов. Целевую сущность часто называют полиморфной сущностью, вызываемый метод — полиморфным методом, сам вызов — полиморфным вызовом.
Вернемся к нашему примеру с классами Found, Derived, chiidDerived. Напомню, в родительском классе определен виртуальный метод 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 переопределяется: