Страница 163 из 502
<b> Console.WriteLine("Метод Who() в классе Base");</b>
<b> }</b>
<b>}</b>
<b>class Derived1 : Base {</b>
<b> // Переопределить метод Who() в производном классе. </b>
<b> public override void Who() {</b>
<b> Console.WriteLine("Метод Who() в классе Derived1");</b>
<b> }</b>
<b>}</b>
<b>class Derived2 : Derived1 {</b>
<b> // В этом классе метод Who() не переопределяется.</b>
<b>}</b>
<b>class Derived3 : Derived2 {</b>
<b> //И в этом классе метод Who() не переопределяется.</b>
<b>}</b>
<b>class No0verrideDemo2 {</b>
<b> static void Main() {</b>
<b> Derived3 dOb = new Derived3();</b>
<b> Base baseRef; // ссылка на базовый класс</b>
<b> baseRef = dOb;</b>
<b> baseRef.Who(); // вызов метода Who() из класса Derivedl</b>
<b> }</b>
<b>}</b>
Вот к какому результату приводит выполнение этого кода.
<b>Метод Who() в классе Derived1</b>
В данном примере класс Derived3 наследует класс Derived2, который наследует класс Derived1, а тот, в свою очередь, — класс Base. Как показывает приведенный выше результат, выполняется метод Who(), переопределяемый в классе Derived1, поскольку это первый вариант виртуального метода, обнаруживаемый при продвижении вверх по иерархии от классов Derived3 и Derived2, где метод Who() не переопределяется, к классу Derived1.
И еще одно замечание: свойства также подлежат модификации ключевым словом virtual и переопределению ключевым словом override. Это же относится и к индексаторам.
Благодаря переопределению методов в C# поддерживается динамический полиморфизм. В объектно-ориентированном программировании полиморфизм играет очень важную роль, потому что он позволяет определить в общем классе методы, которые становятся общими для всех производных от него классов, а в производных классах — определить конкретную реализацию некоторых или же всех этих методов. Переопределение методов — это еще один способ воплотить в C# главный принцип полиморфизма: один интерфейс — множество методов.
Удачное применение полиморфизма отчасти зависит от правильного понимания той особенности, что базовые и производные классы образуют иерархию, которая продвигается от меньшей к большей специализации. При надлежащем применении базовый класс предоставляет все необходимые элементы, которые могут использоваться в производном классе непосредственно. А с помощью виртуальных методов в базовом классе определяются те методы, которые могут быть самостоятельно реализованы в производном классе. Таким образом, сочетая наследование с виртуальными методами, можно определить в базовом классе общую форму методов, которые будут использоваться во всех его производных классах.
Для того чтобы стали понятнее преимущества виртуальных методов, применим их в классе TwoDShape. В предыдущих примерах в каждом классе, производном от класса TwoDShape, определялся метод Area(). Но, по-видимому, метод Area() лучше было бы сделать виртуальным в классе TwoDShape и тем самым предоставить возможность переопределить его в каждом производном классе с учетом особенностей расчета площади той двумерной формы, которую инкапсулирует этот класс. Именно это и сделано в приведенном ниже примере программы. Ради удобства демонстрации классов в этой программе введено также свойство name в классе TwoDShape.
<b>// Применить виртуальные методы и полиморфизм<sup>-</sup>.</b>
<b>using System;</b>
<b>class TwoDShape { </b>
<b> double pri_width;</b>
<b> double pri_height;</b>
<b> // Конструктор по умолчанию,</b>
<b> public TwoDShape() {</b>
<b> Width = Height = 0.0;</b>
<b> name = "null";</b>
<b> }</b>
<b> // Параметризированный конструктор.</b>
<b> public TwoDShape(double w, double h, string n) {</b>
<b> Width = w;</b>
<b> Height = h; </b>
<b> name = n;</b>
<b> }</b>
<b> // Сконструировать объект равной ширины и высоты,</b>
<b> public TwoDShape(double x, string n) {</b>
<b> Width = Height = x;</b>
<b> name = n;</b>
<b> }</b>