Скрытие методов и свойств
В прошлой теме было рассмотрено определение и переопределение виртуальных методов. Другим способом изменить функциональность метода, унаследованного от базового класса, является скрытие (shadowing / hiding).
Фактически скрытие метода/свойства представляет определение в классе-наследнике метода или свойства, которые соответствует по имени и набору параметров методу или свойству базового класса. Для скрытия членов класса применяется ключевое слово new. Например:
class Person
{
public string Name { get; set; }
public Person(string name)
{
Name = name;
}
public void Print()
{
Console.WriteLine($"Name: {Name}");
}
}
class Employee : Person
{
public string Company { get; set; }
public Employee(string name, string company)
: base(name)
{
Company = company;
}
public new void Print()
{
Console.WriteLine($"Name: {Name} Company: {Company}");
}
}
Здесь определен класс Person, представляющий человека, и класс Employee, представляющий работника предприятия. Employee наследует от Person все свойства и методы. Но в классе Employee кроме унаследованных свойств есть также и собственное свойство Company, которое хранит название компании. И мы хотели бы в методе Print выводить информацию о компании вместе с именем на консоль. Для этого определяется метод Print с ключевым словом new, который скрывает реализацию данного метода из базового класса.
В каких ситуациях можно использовать скрытие? Например, в примере выше метод Print в базовом классе не является виртуальным, мы не можем его переопределить, но, допустим, нас не устраивает его реализация для производного класса, поэтому мы можем воспользоваться сокрытием, чтобы определить нужный нам функционал.
Используем эти классы в программе в методе Main:
Person bob = new Person("Bob");
bob.Print(); // Name: Bob
Employee tom = new Employee("Tom", "Microsoft");
tom.Print(); // Name: Tom Company: Microsoft
Консольный вывод программы:
Name: Bob
Name: Tom Company: Microsoft
При этом если мы хотим обратиться именно к реализации свойства или метода в базовом классе, то опять же мы можем использовать ключевое слово base и через него обращаться к функциональности базового класса.
class Employee : Person
{
public string Company { get; set; }
public Employee(string name, string company)
: base(name)
{
Company = company;
}
public new void Print()
{
base.Print(); // вызываем метод Print из базового класса Person
Console.WriteLine($"Company: {Company}");
}
}
Скрытие свойств
Подобным обазом мы можем организовать скрытие свойств:
Person bob = new Person("Bob");
Console.WriteLine(bob.Name); // Bob
Employee tom = new Employee("Tom", "Microsoft");
Console.WriteLine(tom.Name); // Mr./Ms. Tom
class Person
{
public string Name { get; set; }
public Person(string name)
{
Name = name;
}
}
class Employee : Person
{
// скрываем свойство Name базового класса
public new string Name
{
get => $"Mr./Ms. {base.Name}";
set => base.Name = value;
}
public string Company { get; set; }
public Employee(string name, string company)
: base(name)
{
Company = company;
}
}
В данном случае в классе Employee переопределено свойство Name. В блоке get нем мы берем значение свойства из базовового класса P erson и присоединяем к нему "Mr./Ms.". В блоке set передаем полученное значение в реализацию свойства Name базового класса Person
Скрытие переменных и констант
В отличие от переопределения C# позволяет применять скрытие к переменным (как к статическим, так и нестатическим) и константам, также используя ключевое слово new:
Console.WriteLine(Person.minAge); // 1
Console.WriteLine(Person.typeName); // Person
Console.WriteLine(Employee.minAge); // 18
Console.WriteLine(Employee.typeName); // Employee
class Person
{
public readonly static int minAge = 1;
public const string typeName = "Person";
}
class Employee : Person
{
// скрываем поля и константы базового класса
public new readonly static int minAge = 18;
public new const string typeName = "Employee";
}