Язык программирования C#9 и платформа .NET5
}Здесь ключевое слово
ссылается на сигнатуру конструктора (подобно синтаксису, используемому для объединения конструкторов одиночного класса в цепочку через ключевое словоbase, как обсуждалось в главе 5), что всегда указывает производному конструктору на необходимость передачи данных конструктору непосредственного родительского класса. В рассматриваемой ситуации явно вызывается конструктор с шестью параметрами, определенный вthis, что избавляет от излишних обращений во время создания объекта дочернего класса. Кроме того, в классEmployeeдобавлена особая линия поведения, которая заключается в том, что тип оплаты всегда устанавливается вManager. Специальный конструктор классаSalariedвыглядит почти идентично, но только тип оплаты устанавливается вSalesPerson:Commission// В качестве общего правила запомните, что все подклассы должны// явно вызывать подходящий конструктор базового класса.public SalesPerson(string fullName, int age, int empId,float currPay, string ssn, int numbOfSales): base(fullName, age, empId, currPay, ssn,EmployeePayTypeEnum.Commission){// Это принадлежит нам!SalesNumber = numbOfSales;}На заметку! Ключевое слово
можно применять всякий раз, когда подкласс желает обратиться к открытому или защищенному члену, определенному в родительском классе. Использование этого ключевого слова не ограничивается логикой конструктора. Вы увидите примеры применения ключевого словаbaseв подобной манере позже в главе при рассмотрении полиморфизма.baseНаконец, вспомните, что после добавления к определению класса специального конструктора стандартный конструктор молча удаляется. Следовательно, не забудьте переопределить стандартный конструктор для классов
иSalesPerson. Вот пример:Manager// Аналогичным образом переопределите стандартный// конструктор также и в классе Manager.public SalesPerson() {}Хранение секретов семейства: ключевое слово protected
Как вы уже знаете, открытые элементы напрямую доступны отовсюду, в то время как закрытые элементы могут быть доступны только в классе, где они определены. Вспомните из главы 5, что C# опережает многие другие современные объектные языки и предоставляет дополнительное ключевое слово для определения доступности членов —
(защищенный).protectedКогда базовый класс определяет защищенные данные или защищенные члены, он устанавливает набор элементов, которые могут быть непосредственно доступны любому наследнику. Если вы хотите разрешить дочерним классам
иSalesPersonнапрямую обращаться к разделу данных, который определен вManager, то модифицируйте исходный классEmployee(в файлеEmployee), как показано ниже:EmployeeCore.cs// Защищенные данные состояния.partial class Employee{// Производные классы теперь могут иметь прямой доступ к этой информации.protected string EmpName;protected int EmpId;protected float CurrPay;protected int EmpAge;protected string EmpSsn;protected EmployeePayTypeEnum EmpPayType;...}На заметку! По соглашению защищенные члены именуются в стиле Pascal (
), а не в "верблюжьем" стиле с подчеркиванием (EmpName). Это не является требованием языка, но представляет собой распространенный стиль написания кода. Если вы решите обновить имена, как было сделано здесь, тогда не забудьте переименовать все поддерживающие методы в свойствах, чтобы они соответствовали защищенным свойствам с именами в стиле Pascal._empNameПреимущество определения защищенных членов в базовом классе заключается в том, что производным классам больше не придется обращаться к данным косвенно, используя открытые методы и свойства. Разумеется, подходу присущ и недостаток: когда производный класс имеет прямой доступ к внутренним данным своего родителя, то есть вероятность непредумышленного обхода существующих бизнес-правил, которые реализованы внутри открытых свойств. Определяя защищенные члены, вы создаете уровень доверия между родительским классом и дочерним классом, т.к. компилятор не будет перехватывать какие-либо нарушения бизнес-правил, предусмотренных для типа.
Наконец, имейте в виду, что с точки зрения пользователя объекта защищенные данные расцениваются как закрытые (поскольку пользователь находится "снаружи" семейства). По указанной причине следующий код недопустим:
// Ошибка! Доступ к защищенным данным из клиентского кода невозможен!Employee emp = new Employee();emp.empName = "Fred";На заметку! Несмотря на то что защищенные поля данных могут нарушить инкапсуляцию, определять защищенные методы вполне безопасно (и полезно). При построении иерархий классов обычно приходится определять набор методов, которые предназначены для применения только производными типами, но не внешним миром.
Добавление запечатанного класса
Вспомните, что запечатанный класс не может быть расширен другими классами. Как уже упоминалось, такой прием чаще всего используется при проектировании обслуживающих классов. Тем не менее, при построении иерархий классов вы можете обнаружить, что определенная ветвь в цепочке наследования нуждается в "отсечении", т.к. дальнейшее ее расширение не имеет смысла. В качестве примера предположим, что вы добавили в приложение еще один класс (
), который расширяет существующий типPtSalesPerson. Текущее обновление показано на рис. 6.3.SalesPerson