Переопределение методов. Обращение к «затененным» элементам класса
Вызов базового конструктора
Вернемся к конструктору класса Student:
public Student(string n,int year):base(n)
{this.year=year; courses=new List<String>();}
Что означает конструкция :base(n) в заголовке? Дело в том, что конструирование объекта производного класса подразумевает инициализацию унаследованных переменных. С этой работой успешно справляется конструктор производного класса. Однако воспользоваться им в конструкторе производного класса непосредственно не удастся по двум причинам:
1. Конструкторы вообще нельзя вызывать как обычные методы.
2. Конструкторы не наследуются.
Единственным способом заставить работать конструктор базового класса и является конструкция base() в заголовке конструктора производного класса.
В примере видно, что один из параметров – n – используется при вызове base(n), а второй – непосредственно в теле конструктора.
Вызов базового конструктора Вам не понадобится в той редкой ситуации, когда производный объект инициализирует унаследованные переменные не таким образом, как базовый конструктор.
Сейчас в базовом классе Person имеется метод Greeting, возвращающий строку приветствия человека. Этим методом могут пользоваться объекты-студенты, поскольку он унаследован. Однако в реальной жизни молодые люди выполняют свои действия с определенными особенностями. Например, в строке приветствия они могут использовать некоторые «украшения», например вместо «Hi, Peter!” - «Hi, Peter! Ну чё?”
Получается, что базовая реализация метода Greeting нас уже не устраивает и мы переопределим в классе Student метод Greeting точно с таким заголовком (сигнатурой):
public string Greeting(Person p)
{ return Greeting(p) + " Ну чё?"; }
Идея понятна – склеить строку базового приветствия с «украшением». Однако наша программа после запуска и некоторого раздумья выдает ошибку:
Process is terminated due to StackOverflowException.
Дело в том, что сейчас метод Greeting рекурсивно вызывает сам себя и этот процесс бесконечен. Случилось это потому, что новый метод Greeting с той же сигнатурой, что и базовый «заслонил» собой базовый. Это не означает, что базовый метод исчез. Однако для его вызова опять придется использовать ключевое слово base:
public string Greeting(Person p)
{ return base.Greeting(p) + " Ну чё?"; }
Ключевое слово base в данном случае указывает на то, что метод Greeting нужно вызывать из базового класса.
Переопределение методов – очень полезный прием. Но наибольшую пользу он принесет только в форме полиморфного поведения, о чем речь пойдет дальше.
Аналогично можно осуществить и переопределение переменных. Однако это прием используется довольно редко, поскольку быстро вносит неразбериху в ассортимент переменных производных классов.
class Program
{ static void Main(string[] args)
{ A a = new A(); Console.WriteLine(a.x);
B b = new B(); Console.WriteLine(b.x); Console.WriteLine(b.oldX);
}
}
class A { public int x=45; }
class B : A
{ public bool x=true;
public B() { base.x = 34; }
public int oldX { get { return base.x; } }
}
Многоуровневое наследование подразумевает возможность использовать производный класс в качестве базового для определения еще одного производного класса. Например, класс Student может стать основой для создания класса студент-магистр, который должен написать магистерскую работу:
class Magister : Student
{ private string diploma;
public Magister(string name, int year, string diploma)