На этом шаге мы рассмотрим отличие переопределения от перегрузки методов.
Мы познакомились с переопределением метода, когда в производном классе описывается новая версия унаследованного метода. В данном случае речь идет о полном совпадении типа, названия и списка аргументов метода в базовом и производном классах. Но еще есть такой механизм, как перегрузка методов. При перегрузке метода описывается несколько версий этого метода. Для каждой версии название одно и то же, но вот списки аргументов должны отличаться. Эти два механизма (перегрузка и переопределение) могут использоваться одновременно. В примере ниже представлена программа, в которой на фоне наследования используется перегрузка и переопределение методов.
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace pr147_1 { // Базовый класс: class Alpha { // Целочисленное поле: public int alpha; // Метод без аргументов: public void set() { // Значение поля: alpha = 10; // Отображение значения поля: Console.WriteLine("Alpha (без аргументов): {0}", alpha); } // Метод (виртуальный) с одним аргументом: public virtual void set(int n) { // Значение поля: alpha = n; // Отображение значения поля: Console.WriteLine ("Alpha (один: аргумент): {0}", alpha); } } // Производный класс: class Bravo: Alpha { // Целочисленное поле: public int bravo; // Переопределение виртуального метода: public override void set(int n) { // Присваивание значений полям: alpha = n; bravo = alpha; // Отображение значений полей: Console.WriteLine("Bravo (один аргумент): {0} и {1}", alpha, bravo); } // Метод с двумя аргументами: public void set(int m, int n) { // Присваивание значений полям: alpha = m; bravo = n; // Отображение значений полей: Console.WriteLine("Bravo (два аргумента): {0} и {1}", alpha, bravo); } } // Класс с главным методом: class Program { // Главный метод: static void Main() { // Создание объекта базового класса: Alpha A = new Alpha(); // Вызов методов: A.set(); A.set(20); Console.WriteLine(); // Создание объекта производного класса: Bravo B = new Bravo(); // Вызов методов: B.set(); B.set(30); B.set(40, 50); // Задержка: Console.ReadLine(); } } }
Ниже показано, как выглядит результат выполнения программы.
Рис.1. Результат выполнения программы
У нас есть два класса: Alpha и Bravo, причем класс Bravo является производным от класса Alpha. В классе Alpha имеется открытое целочисленное поле alpha, которое наследуется в классе Bravo. Еще в классе Bravo появляется целочисленное поле bravo. В классе Alpha описаны две версии метода set(): без аргументов и с одним аргументом. Версия метода set() с одним аргументом описана как виртуальная (использовано ключевое слово virtual). В классе Bravo появляется еще одна версия метода set() с двумя аргументами, а версия метода с одним аргументом переопределяется (она описана с ключевым словом override). Каждая версия метода описана так, что полю или полям присваиваются значения, после чего в консольное окно выводится сообщение с указанием названия класса, количества аргументов у метода и фактического значения поля или полей объекта. Таким образом, по сообщению, отображаемому в консольном окне при вызове метода, можно однозначно определить, какая версия метода была вызвана.
В главном методе программы мы создаем объект А класса Alpha и объект B класса Bravo. Из каждого объекта вызываются разные версии метода set(). Для объекта А это две версии (обе описаны в классе Alpha): без аргументов и с одним аргументом. Для объекта В таких версий три. Это, во-первых, версия метода без аргументов, унаследованная из класса Alpha. Во-вторых, переопределенная в классе Bravo версия метода с одним аргументом. И в-третьих, версия с двумя аргументами, описанная в классе Bravo.
На следующем шаге мы рассмотрим наследование свойств и индексаторов.