Виртуальным называется такой метод, который объявляется как virtual в базовом классе. Виртуальный метод отличается тем, что он может быть переопределен в одном или нескольких производных классах. Следовательно, у каждого производного класса может быть свой вариант виртуального метода. Кроме того, виртуальные методы интересны тем, что именно происходит при их вызове по ссылке на базовый класс. В этом случае средствами языка C# определяется именно тот вариант виртуального метода, который следует вызывать, исходя из типа объекта, к которому происходит обращение по ссылке, причем это делается во время выполнения. Иными словами, вариант выполняемого виртуального метода выбирается по типу объекта, а не по типу ссылки на этот объект.
Метод объявляется как виртуальный в базовом классе с помощью ключевого слова virtual, указываемого перед его именем. Когда же виртуальный метод переопределяется в производном классе, то для этого используется модификатор override. А сам процесс повторного определения виртуального метода в производном классе называется переопределением метода. При переопределении имя, возвращаемый тип и сигнатура переопределяющего метода должны быть точно такими же, как и у того виртуального метода, который переопределяется. Кроме того, виртуальный метод не может быть объявлен как static или abstract.
Переопределение метода служит основанием для воплощения одного из самых эффективных в C# принципов: динамической диспетчеризации методов, которая представляет собой механизм разрешения вызова во время выполнения, а не компиляции. Значение динамической диспетчеризации методов состоит в том, что именно благодаря ей в C# реализуется динамический полиморфизм.
//Продемонстрировать виртуальный метод.
using System;
class Base
{
// Создать виртуальный метод в базовом классе.
public virtual void Who()
{
Console.WriteLine("Метод Who() в классе Base");
}
}
class Derived1: 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();
Derived1 dOb1 = new Derived1();
Derived2 dOb2 = new Derived2();
Base baseRef;//ссылка на базовый класс
baseRef = baseOb;
baseRef.Who();
baseRef = dOb1;
baseRef.Who();
baseRef = dOb2;
baseRef.Who();
}
}
Метод Who() в классе Base.
Метод Who() в классе Derived1
Метод Who() в классе Derived2
В коде из приведенного выше примера создаются базовый класс Baseи два производных от него класса — Derived1 и Derived2. В классе Base объявляется виртуальный метод Who(), который переопределяется в обоих производных классах.
Но переопределять виртуальный метод совсем не обязательно. Ведь если в производном классе не предоставляется собственный вариант виртуального метода, то используется его вариант из базового класса, как в приведенном ниже примере.
/* Если виртуальный метод не переопределяется, то
используется его вариант из базового класса. */
using System;
class Base
{
// Создать виртуальный метод в базовом классе.
public virtual void Who()
{
Console.WriteLine("Метод Who() в классе Base");
}
}
class Derived1: Base
{
// Переопределить метод Who() в производном классе.
public override void Who()
{
Console.WriteLine("Метод Who() в классе Derived1");
}
}
class Derived2: Base
{
// В этом классе метод Who() не переопределяется.
}
class NoOverrideDemo
{
static void Main()
{
Base baseOb = new Base();
Derived1 dOb1 = new Derived1();
Derived2 dOb2 = new Derived2();
Base baseRef; // ссылка на базовый класс
baseRef = baseOb;
baseRef.Who();
baseRef = dOb1;
baseRef.Who();
baseRef = dOb2;
baseRef.Who(); // вызывается метод Who() из класса Base
}
}
Метод Who() в классе Base.
Метод Who() в классе Derived1
Метод Who() в классе Base
Если при наличии многоуровневой иерархии виртуальный метод не переопределяется в производном классе, то выполняется ближайший его вариант, обнаруживаемый вверх по иерархии, как в приведенном ниже примере.
/* В многоуровневой иерархии классов выполняется тот переопределенный вариант
виртуального метода, который обнаруживается первым при продвижении вверх по иерархии. */
using System;
class Base
{
// Создать виртуальный метод в базовом классе.
public virtual void Who()
{
Console.WriteLine("Метод Who() в классе Base");
}
}
class Derived1: Base
{
// Переопределить метод Who() в производном классе.
public override void Who()
{
Console.WriteLine("Метод Who() в классе Derived1");
}
}
class Derived2: Derived1
{
// В этом классе метод Who() не переопределяется.
}
class Derived3: Derived2
{
// И в этом классе метод Who() не переопределяется.
}
class NoOverrideDemo2
{
static void Main()
{
Derived3 dOb = new Derived3();
Base baseRef; // ссылка на базовый класс
baseRef = dOb;
baseRef.Who(); // вызов метода Who() из класса Derived1
}
}
Метод Who() в классе Derivedl
И еще одно замечание: свойства также подлежат модификации ключевым словом virtual и переопределению ключевым словом override. Это же относится и к индексаторам.