На этом шаге мы рассмотрим отличие переопределения от замещения.
Теперь пришел черед выяснить, чем замещение методов отличается от переопределения виртуальных методов. Для большей наглядности сразу рассмотрим пример. Он будет несложным, но показательным. Идея, положенная в основу примера, простая. Имеется базовый класс Alpha, в котором есть два метода: hi() и hello(). Метод hi() виртуальный, а метод hello() обычный (не виртуальный). А еще в классе Alpha есть метод show(). Это обычный (не виртуальный) метод. При его вызове последовательно вызываются методы hello() и hi(). Далее, на основе класса Alpha создается производный класс Bravo. В классе Bravo замещается метод hello() и переопределяется метод hi(). При этом метод show() просто наследуется. Таким образом, в классе Bravo есть унаследованный метод show(), при вызове которого вызываются методы hello() и hi(). Интрига связана с тем, в каких ситуациях какие версии этих методов будут вызываться. А теперь рассмотрим программный код, представленный ниже.
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace pr146_1 { // Базовый класс: class Alpha { // Обычный (не виртуальный) метод: public void hello() { Console.WriteLine("Метод hello() из класса Alpha"); } // Виртуальный метод: public virtual void hi() { Console.WriteLine("Метод hi() из класса Alpha"); } // Обычный (не виртуальный) метод: public void show() { // Вызов методов: hello(); hi(); Console.WriteLine(); } } // Производный класс: class Bravo: Alpha{ // Замещение метода: new public void hello() { Console.WriteLine("Метод hello() из класса Bravo"); } // Переопределение виртуального метода: public override void hi() { Console.WriteLine("Метод hi() из класса Bravo"); } } // Класс с главным методом: class Program { // Главный метод: static void Main() { Bravo A = new Bravo(); Console.WriteLine("Выполнение команды A.show():"); // Вызов метода: A.show(); // Создание объекта производного класса: Bravo B = new Bravo(); Console.WriteLine("Выполнение команды B.hello():"); // Вызов замещающего метода из производного класса: B.hello(); Console.WriteLine("Выполнение команды В.hi():"); // Вызов переопределенного метода: B.hi(); Console.WriteLine("Выполнение команды В.show():"); // Вызов унаследованного метода: B.show(); Console.WriteLine("После выполнения команды А=В"); // Объектной переменной базового класса присваивается // ссылка на объект производного класса: A = B; Console.WriteLine("Выполнение команды A.hello():"); // Вызов замещаемого метода: A.hello(); Console.WriteLine("Выполнение команды A.hi():"); // Вызов переопределенного метода: A.hi(); Console.WriteLine("Выполнение команды A.show():"); // Вызов унаследованного метода: A.show(); // Задержка: Console.ReadLine(); } } }
Результат выполнения программы такой.
Рис.1. Результат выполнения программы
В главном методе сначала создается объект класса Alpha, и ссылка на него записывается в объектную переменную А того же класса. Результат вызова метода show() через эту переменную вполне ожидаем: вызываются те версии методов hello() и hi(), которые описаны в классе Alpha.
На следующем этапе создается объект класса Bravo и ссылка на объект записывается в объектную переменную В класса Bravo. При выполнении команд
B.hello();
B.hi();
B.show(); .
После выполнения команды
A = B;
A.hello(); ,
A.hi();
A.show();
На следующем шаге мы рассмотрим переопределение и перегрузку методов.