Вы наверняка заметили, что при переопределении члена класса приходится вспоминать тип каждого параметра, не говоря уже об имени метода и соглашениях по передаче параметров (ref
out
и params
). В Visual Studio и Visual Studio Code доступно полезное средство IntelliSense
, к которому можно обращаться при переопределении виртуального члена. Если вы наберете слово override
внутри области действия типа класса (и затем нажмете клавишу пробела), то IntelliSense
автоматически отобразит список всех допускающих переопределение членов родительского класса, исключая уже переопределенные методы.Если вы выберете член и нажмете клавишу <Enter
>, то IDE-среда отреагирует автоматическим заполнением заглушки метода. Обратите внимание, что вы также получаете оператор кода, который вызывает родительскую версию виртуального члена (можете удалить эту строку, если она не нужна). Например, при использовании описанного приема для переопределения методаDisplayStats()
вы обнаружите следующий автоматически сгенерированный код:public override void DisplayStats()
{
base.DisplayStats();
}
Запечатывание виртуальных членов
Вспомните, что к типу класса можно применить ключевое слово sealed
PtSalesPerson
был запечатан на основе предположения о том, что разработчикам не имеет смысла дальше расширять эту линию наследования.Следует отметить, что временами желательно не запечатывать класс целиком, а просто предотвратить переопределение некоторых виртуальных методов в производных типах. В качестве примера предположим, что вы не хотите, чтобы продавцы с частичной занятостью получали специальные бонусы. Предотвратить переопределение виртуального метода GiveBonus()
PtSalesPerson
можно, запечатав данный метод в классе SalesPerson
:// Класс SalesPerson запечатал метод GiveBonus()!
class SalesPerson : Employee
{
...
public override sealed void GiveBonus(float amount)
{
...
}
}
Здесь класс SalesPerson
GiveBonus()
, определенный в Employee
, но явно помечает его как sealed
. Таким образом, попытка переопределения метода GiveBonus()
в классе PtSalesPerson
приведет к ошибке на этапе компиляции:sealed class PTSalesPerson : SalesPerson
{
...
// Ошибка на этапе компиляции! Переопределять этот метод
// в классе PtSalesPerson нельзя, т.к. он был запечатан.
{
}
}
Абстрактные классы
В настоящий момент базовый класс Employee
GiveBonus()
и DisplayStats()
), которые могут быть переопределены в наследниках. Хотя все это замечательно, у такого проектного решения имеется один весьма странный побочный эффект: создавать экземпляры базового класса Employee
можно напрямую:// Что это будет означать?
Employee X = new Employee();
В нашем примере базовый класс Employee
Employee
, т.к. он концептуально чересчур общий. Например, если кто-то заявит, что он сотрудник, то тут же возникнет вопрос: сотрудник какого Учитывая, что многие базовые классы имеют тенденцию быть довольно расплывчатыми сущностями, намного более эффективным проектным решением для данного примера будет предотвращение возможности непосредственного создания в коде нового объекта Employee
abstract
в определении класса, создавая в итоге // Превращение класса Employee в абстрактный для
// предотвращения прямого создания его экземпляров.
abstract partial class Employee
{
...
}
Теперь попытка создания экземпляра класса Employee
// Ошибка! Нельзя создавать экземпляр абстрактного класса!
Employee X = new Employee();