Полиморфизм является третьим ключевым аспектом объектно-ориентированного программирования и предполагает способность к изменению функционала, унаследованного от базового класса. Полиморфизм предполагает определение полиморфного интерфейса в базовом классе - набор членов класса, которые могут быть переопределены в классе-наследнике. Методы, которые мы хотим сделать доступными для переопределения, в базовом классе помечается модификатором virtual. Такие методы называют виртуальными. Они и представляют полиморфный интерфейс (также частью полиморфного интерфейса могут быть абстрактные члены класса, о которых рассказывается в следующей теме).
При определении класса-наследника и наследовании методов базового класса мы можем выбрать одну из следующих стратегий:
· Обычное наследование всех членов базового класса в классе-наследнике
· Переопределение членов базового класса в классе-наследнике
· Скрытие членов базового класса в классе-наследнике
Первая стратегия довольно проста. Допустим, есть следующая пара классов Person и Employee:
|
|
class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public Person(string lName, string fName)
{
FirstName = fName;
LastName = lName;
}
public virtual void Display()
{
Console.WriteLine(FirstName + " " + LastName);
}
}
class Employee: Person
{
public string Company { get; set; }
public Employee(string lName, string fName, string comp)
:base(fName, lName)
{
Company = comp;
}
}
В базовом классе Person метод Display() определен с модификаторами virtual, поэтому данный метод может быть переопределен. Но класс Employee наследует его как есть:
class Program
{
static void Main(string[] args)
{
Person p1 = new Person("Bill", "Gates");
p1.Display(); // вызов метода Display из класса Person
Person p2 = new Employee("Tom", "Johns", "UnitBank");
p2.Display(); // вызов метода Display из класса Person
Employee p3 = new Employee("Sam", "Toms", "CreditBank");
p3.Display(); // вызов метода Display из класса Person
Console.Read();
}
}
Консольный вывод:
Bill GatesTom JohnsSam TomsВторая стратегия - переопределение методов базового класса в классе-наследнике предполагает использование ключевого слова override:
class Employee: Person
{
public string Company { get; set; }
public Employee(string lName, string fName, string comp)
:base(fName, lName)
{
Company = comp;
}
public override void Display()
{
Console.WriteLine(FirstName + " " + LastName + " работает в компании "+ Company);
}
}
Класс Person остается тем же, в нем так же метод Display объявляется как виртуальный. В этом случае поведение объекта Employee изменится:
Person p1 = new Person("Bill", "Gates");
p1.Display(); // вызов метода Display из класса Person
Person p2 = new Employee("Tom", "Johns", "UnitBank");
|
|
p2.Display(); // вызов метода Display из класса Employee
Employee p3 = new Employee("Sam", "Toms", "CreditBank");
p3.Display(); // вызов метода Display из класса Employee
Консольный вывод:
Bill GatesTom Johns работает в компании UnitBankSam Toms работает в компании CreditBankПри третьей стратегии можно просто определить в классе-наследнике метод с тем же именем, добавив в его определение ключевое слово new:
class Employee: Person
{
public string Company { get; set; }
public Employee(string lName, string fName, string comp)
:base(fName, lName)
{
Company = comp;
}
public new void Display()
{
Console.WriteLine(FirstName + " " + LastName + " работает в компании "+ Company);
}
}
В этом случае метод Display() в Employee скрывает метод Display() из класса Person. Чтобы явно скрыть метод из базового класса, используется ключевое слово new, хотя в принципе оно необязательно, по умолчанию система это делает неявно.
Использование в программе:
Person p1 = new Person("Bill", "Gates");
p1.Display(); // вызов метода Display из класса Person
Person p2 = new Employee("Tom", "Johns", "UnitBank");
p2.Display(); // вызов метода Display из класса Person
Employee p3 = new Employee("Sam", "Toms", "CreditBank");
p3.Display(); // вызов метода Display из класса Employee
Консольный вывод:
Bill GatesTom JohnsSam Toms работает в компании CreditBankОбратите внимание на различия во втором случае (p2.Display()) при разных стратегиях.
Ключевое слово base
Кроме конструкторов, мы можем обратиться с помощью ключевого слова base к другим членам базового класса. В нашем случае вызов base.Display(); будет обращением к методу Display() в классе Person:
class Employee: Person
{
public string Company { get; set; }
public Employee(string lName, string fName, string comp)
:base(fName, lName)
{
Company = comp;
}
public override void Display()
{
base.Display();
Console.WriteLine("Место работы: " + Company);
}
}
Запрет переопределения методов
Также можно запретить переопределение методов и свойств. В этом случае их надо объявлять с модификатором sealed:
class Employee: Person
{
public string Company { get; set; }
public Employee(string lName, string fName, string comp)
:base(fName, lName)
{
Company = comp;
}
public override sealed void Display()
{
base.Display();
Console.WriteLine("Место работы: " + Company);
}
}
При создании методов с модификатором sealed надо учитывать, что sealed применяется в паре с override, то есть только в переопределяемых методах.
И в этом случае мы не сможем переопределить метод Display в классе, унаследованном от Employee.