Язык программирования C#9 и платформа .NET5
// Manager.cspublic override void DisplayStats(){base.DisplayStats();// Вывод количества фондовых опционовConsole.WriteLine("Number of Stock Options: {0}", StockOptions);}// SalesPerson.cspublic override void DisplayStats(){base.DisplayStats();// Вывод количества продажConsole.WriteLine("Number of Sales: {0}", SalesNumber);}Теперь, когда каждый подкласс может истолковывать эти виртуальные методы значащим для него образом, их экземпляры ведут себя как более независимые сущности:
Console.WriteLine("***** The Employee Class Hierarchy *****\n");// Лучшая система бонусов!Manager chucky = new Manager("Chucky", 50, 92, 100000, "333-23-2322", 9000);chucky.GiveBonus(300);chucky.DisplayStats();Console.WriteLine();SalesPerson fran = new SalesPerson("Fran", 43, 93, 3000, "932-32-3232", 31);fran.GiveBonus(200);fran.DisplayStats();Console.ReadLine();Вот результат тестового запуска приложения в его текущем виде:
***** The Employee Class Hierarchy *****Name: ChuckyID: 92Age: 50Pay: 100300SSN: 333-23-2322Number of Stock Options: 9337Name: FranID: 93Age: 43Pay: 5000SSN: 932-32-3232Number of Sales: 31Переопределение виртуальных членов с помощью Visual Studio/Visual Studio Code
Вы наверняка заметили, что при переопределении члена класса приходится вспоминать тип каждого параметра, не говоря уже об имени метода и соглашениях по передаче параметров (
,refиout). В Visual Studio и Visual Studio Code доступно полезное средствоparams, к которому можно обращаться при переопределении виртуального члена. Если вы наберете слово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()приведет к ошибке на этапе компиляции:PtSalesPersonsealed class PTSalesPerson : SalesPerson{...// Ошибка на этапе компиляции! Переопределять этот метод<div class="fb2-code"><code> // в классе PtSalesPerson нельзя, т.к. он был запечатан.</code></div>{}}Абстрактные классы
В настоящий момент базовый класс
спроектирован так, что поставляет различные данные-члены своим наследникам, а также предлагает два виртуальных метода (EmployeeиGiveBonus()), которые могут быть переопределены в наследниках. Хотя все это замечательно, у такого проектного решения имеется один весьма странный побочный эффект: создавать экземпляры базового классаDisplayStats()можно напрямую:Employee