C# 4.0 полное руководство - 2011
Шрифт:
Виртуальные методы и их переопределение
Виртуальным называется такой метод, который объявляется как virtual в базовом классе. Виртуальный метод отличается тем, что он может быть переопределен в одном или нескольких производных классах. Следовательно, у каждого производного класса
может быть свой вариант виртуального метода. Кроме того, виртуальные методы интересны тем, что именно происходит при их вызове по ссылке на базовый класс. В этом случае средствами языка C# определяется именно тот вариант виртуального метода, который следует вызывать,
Метод объявляется как виртуальный в базовом классе с помощью ключевого слова virtual, указываемого перед его именем. Когда же виртуальный метод переопределяется в производном классе, то для этого используется модификатор override. А сам процесс повторного определения виртуального метода в производном классе называется переопределением метода. При переопределении имя, возвращаемый тип и сигнатура переопределяющего метода должны быть точно такими же, как и у того виртуального метода, который переопределяется. Кроме того, виртуальный метод не может быть объявлен как static или abstract (подробнее данный вопрос рассматривается далее в этой главе).
Переопределение метода служит основанием для воплощения одного из самых эффективных в C# принципов: динамической диспетчеризации методов, которая представляет собой механизм разрешения вызова во время выполнения, а не компиляции. Значение динамической диспетчеризации методов состоит в том, что именно благодаря ей в C# реализуется динамический полиморфизм.
Ниже приведен пример, демонстрирующий виртуальные методы и их переопределение.
// Продемонстрировать виртуальный метод.
using System;
class Base {
// Создать виртуальный метод в базовом классе, public virtual void Who {
Console.WriteLine("Метод Who в классе Base");
}
}
class Derivedl : Base {
// Переопределить метод Who в производном классе, public override void Who {
Console.WriteLine("Метод Who в классе Derivedl");
}
}
class Derived2 : Base {
// Вновь переопределить метод Who в еще одном производном классе, public override void Who {
Console.WriteLine("Метод Who в классе Derived2");
class OverrideDemo { static void Main {
Base baseOb = new Base;
Derivedl dObl = new DerivedlO;
Deri'ved2 dOb2 = new Derived2;
Base baseRef; //
baseRef = baseOb; baseRef.Who ;
baseRef = dObl; baseRef.Who;
baseRef = d0b2; baseRef.Who;
}
}
Вот к какому результату приводит выполнение этого кода.
Метод Who в классе Base.
Метод Who в классе Derivedl Метод Who в классе Derived2
В коде из приведенного выше примера создаются базовый класс Base и два производных от него класса — Derivedl и Derived2. В классе Base объявляется виртуальный метод Who , который переопределяется в обоих производных классах. Затем в методе Main объявляются объекты типа Base, Derivedl и Derived2. Кроме того, объявляется переменная baseRef ссылочного типа Base. Далее ссылка на каждый тип объекта присваивается переменной baseRef и затем используется для вызова метода Who . Как следует из результата выполнения приведенного выше кода, вариант выполняемого метода Who определяется по типу объекта, к которому происходит обращение по ссылке во время вызова этого метода, а не по типу класса переменной baseRef.
Но переопределять виртуальный метод совсем не обязательно. Ведь если в производном классе не предоставляется собственный вариант виртуального метода, то используется его вариант из базового класса, как в приведенном ниже примере.
/* Если виртуальный метод не переопределяется, то используется его вариант из базового класса. */
using System;
class Base {
// Создать виртуальный метод в базовом классе. public virtual void Who {
Console.WriteLine("Метод Who в классе Base");
}
}
class Derivedl : Base {
// Переопределить метод Who в производном классе.
public override void Who {
Console.WriteLine("Метод Who в классе Derivedl");
}
}
class Derived2 : Base {
// В этом классе метод Who не переопределяется.
}
class NoOverrideDemo { static void Main {
Base baseOb = new Base;
Derivedl dObl = new Derivedl;
Derived2 d0b2 = new Derived2;
Base baseRef; // ссылка на базовый класс
baseRef = baseOb; baseRef.Who;
baseRef = dObl ; baseRef.Who ;
baseRef = d0b2;
• baseRef.Who; // вызывается метод Who из класса Base }