Виртуальные методы и свойства в С #
При наследовании может возникнуть необходимость изменить в классе-наследнике функционал метода, который был унаследован от базового класса. В этом случае класс-наследник может переопределять методы и свойства базового класса.
Те методы и свойства, которые будут доступными для переопределения, в базовом классе помечаются модификатором virtual . Такие методы и свойства называют виртуальными .
А чтобы переопределить метод в классе-наследнике, этот метод определяется с модификатором override . Переопределенный метод в классе-наследнике должен иметь тот же набор параметров, что и виртуальный метод в базовом классе.
class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public Person(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
public virtual void Display()
{
Console.WriteLine($"{FirstName} {LastName}");
}
}
class Employee : Person
{
public string Company { get; set; }
public Employee(string firstName, string lastName, string company)
: base(firstName, lastName)
{
Company = company;
}
}
Здесь класс Person представляет человека. Класс Employee наследуется от Person и представляет сотруднника предприятия. Этот класс кроме унаследованных свойств имеет еще одно свойство - Company.
Чтобы сделать метод Display доступным для переопределения, этот метод определен с модификатором virtual . Поэтому мы можем переопределить этот метод. Допустим, нас устраивает реализация метода из базового класса. В этом случае объекты Employee будут использовать реализацию метода Display из класса Person:
static void Main(string[] args)
{
Person p1 = new Person("Bill", "Gates");
p1.Display(); // вызов метода Display из класса Person
Employee p2 = new Employee("Tom", "Smith", "Microsoft");
p2.Display(); // вызов метода Display из класса Person
Console.ReadKey();
}
Консольный вывод:
Bill Gates
Tom Smith работает в Microsoft
Виртуальные методы базового класса определяют интерфейс всей иерархии, то есть в любом производном классе, который не является прямым наследником от базового класса, можно переопределить виртуальные методы. Например, мы можем определить класс Manager, который будет производным от Employee, и в нем также переопределить метод Display.
При переопределении виртуальных методов следует учитывать ряд ограничений:
- Виртуальный и переопределенный методы должны иметь один и тот же модификатор доступа. То есть есть если виртуальный метод определен с помощью модификатора public, то и переопредленный метод также должен иметь модификатор public.
- Нельзя переопределить или объявить виртуальным статический метод.
Также как и методы, можно переопределять свойства :
class Credit
{
public virtual decimal Sum { get; set; }
}
class LongCredit : Credit
{
private decimal sum;
public override decimal Sum
{
get
{
return sum;
}
1000) { sum = value; } } } } class Program { static void Main(string[] args) { LongCredit credit = new LongCredit { Sum = 6000 }; " width="640"
set
{
if(value 1000)
{
sum = value;
}
}
}
}
class Program
{
static void Main(string[] args)
{
LongCredit credit = new LongCredit { Sum = 6000 };
credit.Sum = 490;
Console.WriteLine(credit.Sum);
Console.ReadKey();
}
}
Кроме конструкторов, мы можем обратиться с помощью ключевого слова base к другим членам базового класса. В нашем случае вызов base.Display(); будет обращением к методу Display() в классе Person:
class Employee : Person
{
public string Company { get; set; }
public Employee(string lastName, string firstName, string company)
:base(firstName, lastName)
{
Company = company;
}
public override void Display()
{
base.Display();
Console.WriteLine($" работает в {Company}");
}
}
Запрет переопределения методов
Можно запретить переопределение методов и свойств. В этом случае их надо объявлять с модификатором sealed :
class Employee : Person
{
public string Company { get; set; }
public Employee(string firstName, string lastName, string company)
: base(firstName, lastName)
{
Company = company;
}
public override sealed void Display()
{
Console.WriteLine($"{FirstName} {LastName} работает в {Company}");
}
}
При создании методов с модификатором sealed надо учитывать, что sealed применяется в паре с override , то есть только в переопределяемых методах.
И в этом случае мы не сможем переопределить метод Display в классе, унаследованном от Employee.
Сокрытие
Ранее было рассмотрено определение и переопределение виртуальных методов. Другим способом изменить функциональность метода, унаследованного от базового класса, является сокрытие .
Сокрытие представляет определение в классе-наследнике метода или свойства, которые соответствует по имени и набору параметров методу или свойству базового класса. Для сокрытия членов класса применяется ключевое слово new .
Если в базовом классе метод не был объявлен виртуальным, в производном классе все равно можно объявить другой метод с такой же сигнатурой. Новый метод скроет метод базового класса.
При этом компилятор, решая, какой метод вызвать, всегда будет рассматривать тип данных, на который указывает переменная, как тип данных заданный при ее объявлении.
Скрытие (переопределение) элементов класса – это явное описание в классе-наследнике с новыми характеристиками уже существующих элементов из наследуемого класса.
public class MyClass
{
public int MyNumber;
}
public class OurClass : MyClass
{
public double MyNumber;
}
Переопределено поле MyNumber (в родительском классе имело целочисельный тип, а в производном – дробный). Поле целого типа скрыто новым описанием.
Если бы поле MyNumber не было переопределено, то в классе OurClass (производный) по умолчанию было бы доступен только родительское поле MyNumber типа int. А выражение: oc.MyNumber = 5.5; вызывало ошибку типов.
Если метод скрывает метод или свойство базового класса, то к его определению необходимо добавить ключевое слово new .
Этот модификатор подскажет компилятору о факте сокрытия .
public class Customer
{
public string GetFunnyString()
{
return "Cusomer funny!";
}
}
...
public class Nevermore60Customer : Customer
{
public new string GetFunnyString()
{
return "Nevermore60 funny!”;
}
}
...
Customer Cust1;
Nevermore60Customer Cust2; Cust1 = new Customer();
Console.WriteLine(Cust1.GetFunnyString());
Cust1 = new Nevermore60Customer();
Console.WriteLine(Cust1.GetFunnyString());
Cust2 = new Nevermore60Customer(); Console.WriteLine(Cust2.GetFunnyString());
...
}
Результат:
Cusomer funny! Cusomer funny! Nevermore60 funny!