1629295407-c61bfe4caba98380ea3e7cdae6295416 (846200), страница 55
Текст из файла (страница 55)
Язык C#Console.WriteLine("Площадь равна " + t1.area());Console.WriteLine();}}Console.WriteLine("Информация о t2: ");t2.showStyle();t2.showDim();Console.WriteLine("Площадь равна " + t2.area());В этой программе объект t2 создается из объекта t1 и является идентичным ему.Вот результаты выполнения этой программы:Информация о t1:Треугольник прямоугольныйШирина и высота равны 8 и 12Площадь равна 48Информация о t2:Треугольник прямоугольныйШирина и высота равны 8 и 12Площадь равна 4 8Обратите внимание на этот конструктор класса Triangle:// Создаем объект из объекта.public Triangle(Triangle ob) : base(ob) {style = ob.style;}Он принимает объект типа Triangle и передает его (посредством base-механизма)этому конструктору класса TwoDShape:// Создаем объект из объекта.public TwoDShape(TwoDShape ob) {width = ob.width;height = ob.height;}Ключевым моментом здесь является то, что конструктор TwoDShape() ожидаетобъект класса TwoDShape.
Однако конструктор Triangle() передает ему объект классаTriangle. Как разъяснялось выше, такой “номер проходит” благодаря тому, что ссылка набазовый класс может указывать на объект производного класса. Следовательно, вполнедопустимо передать конструктору TwoDShape() ссылку на объект класса, выведенного изкласса TwoDShape. Поскольку конструктор TwoDShape() инициализирует только течасти объекта производного класса, которые являются членами класса TwoDShape, неимеет значения, что объект может содержать и другие члены, добавленные производнымклассом.Виртуальные методы и их переопределениеВиртуальным называется метод, объявляемый с помощью ключевого слова virtualв базовом классе и переопределяемый в одном или нескольких производных классах.
Такимобразом, каждый производный класс может иметь собственную версию виртуальногометода. Виртуальные методы представляют интерес с такой позиции: что произойдет, есливиртуальный метод будет вызван посредством ссылки на базовый класс. Какую именноверсию метода нужно вызвать, C# определяет по типуГлава 11. Наследование301объекта, на который указывает эта ссылка, причем решение принимается динамически, вовремя выполнения программы.
Следовательно, если имеются ссылки на различные объекты,будут выполняться различные версии виртуального метода. Другими словами, именно типобъекта, на который указывает ссылка (а не тип ссылки) определяет, какая версиявиртуального метода будет выполнена. Таким образом, если базовый класс содержитвиртуальный метод и из этого класса выведены производные классы, то при наличииссылки на различные типы объектов (посредством ссылки на базовый класс) будутвыполняться различные версии этого виртуального метода.Чтобы объявить метод в базовом классе виртуальным, его объявление необходимопредварить ключевым словом virtual. При переопределении виртуального метода впроизводном классе используется модификатор override.
Итак, процесс переопределениявиртуального метода в производном классе иногда называется замещением метода (methodoverriding). При переопределении метода сигнатуры типа у виртуального и методазаменителя должны совпадать. Кроме того, виртуальный метод нельзя определять какстатический (с использованием слова 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() в классе Derived1");}}class Derived2 : Base {// Снова переопределяем метод who()// в другом производном классе.public override void who() {Console.WriteLine("Метод who() в классе Derived2");}}class OverrideDemo {public static void Main() {Base baseOb = new Base();Derived1 dOb1 = new Derived1();Derived2 dOb2 = new Derived2();302Часть I.
Язык C#}}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(), а производные классыего переопределяют. В методе Main() объявляются объекты типа Base, Derived1 иDerived2, а также ссылка baseRef типа Base.
Затем программа поочередно присваиваетссылку на объект каждого типа ссылке baseRef и использует эту ссылку для вызоваметода who(). Как показывают результаты выполнения этой программы, нужная длявыполнения версия определяется типом объекта, адресуемого в момент вызова, а не“классовым” типом ссылки baseRef.Виртуальный метод переопределять необязательно.
Если производный класс непредоставляет собственную версию виртуального метода, используется версия,определенная в базовом классе. Вот пример:/* Если виртуальный метод не переопределенв производном классе, используется методбазового класса. */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 {public static void Main() {Base baseOb = new Base();Derived1 dOb1 = new Derived1();Глава 11.
Наследование303}}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Здесь класс Derived2 не переопределяет метод who(). Поэтому при вызове методаwho() для объекта класса Derived2 выполняется метод 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 {304Часть I. Язык C#public static void Main() {Derived3 dOb = new Derived3();}}Base baseRef; // Ссылка на базовый класс.baseRef = dOb;baseRef.who();// Вызывает метод who()//из класса Derived1.Результаты выполнения этой программы таковы:Метод who() в классе Derived1Здесь класс Derived3 наследует класс Derived2, который наследует классDerived1, который в свою очередь наследует класс Base.
Как подтверждают результатывыполнения этой программы, поскольку метод who() не переопределяется ни в классеDerived3, ни в классе Derived2, но переопределяется в классе Derived1, то именноэта версия метода who() (из класса Derived1) и выполняется, так как она являетсяпервой обнаруженной в иерархии классов.Еще одно замечание. Свойства также можно модифицировать с помощью ключевогослова virtual, а затем переопределять с помощью ключевого слова override.Зачем переопределять методыПереопределение методов позволяет C# поддерживать динамический полиморфизм.Без полиморфизма объектно-ориентированное программирование невозможно, посколькуон позволяет исходному классу определять общие методы, которыми будут пользоватьсявсе производные классы, и в которых при этом можно будет задать собственнуюреализацию некоторых или всех этих методов.
Переопределенные методы представляютсобой еще один способ реализации в C# аспекта полиморфизма, который можно выразитькак “один интерфейс — много методов”.Ключ (вернее, его первый “поворот”) к успешному применению полиморфизмалежит в понимании того, что базовые и производные классы образуют иерархию, котораяразвивается в сторону более узкой специализации. При корректном использовании базовыйкласс предоставляет производному классу все элементы “пригодными к употреблению”, т.е.для прямого их использования. Кроме того, он определяет методы, которые производныйкласс должен реализовать самостоятельно.