Язык программирования C#9 и платформа .NET5
Хотя все это хорошо, но необходимо также принимать во внимание и то, что обычно происходит внутри конструктора класса. Конструктор получает входные параметры, проверяет данные на предмет допустимости и затем присваивает значения внутренним закрытым полям. Пока что главный конструктор не проверяет входные строковые данные на вхождение в диапазон допустимых значений, а потому его можно было бы изменить следующим образом:
public Employee(string name, int age, int id, float pay){/// Похоже на проблему. ..if (name.Length > 15){Console.WriteLine("Error! Name length exceeds 15 characters!");// Ошибка! Длина имени превышает 15 символов!}else{_empName = name;}_empId = id;_empAge = age;_currPay = pay;}Наверняка вы заметили проблему, связанную с таким подходом. Свойство
и главный конструктор выполняют одну и ту же проверку на наличие ошибок. Реализуя проверки для других элементов данных, есть реальный шанс столкнуться с дублированием кода. Стремясь рационализировать код и изолировать всю проверку, касающуюся ошибок, в каком-то центральном местоположении, вы добьетесь успеха, если для получения и установки значений внутри класса всегда будете применять свойства. Взгляните на показанный ниже модифицированный конструктор:Namepublic Employee(string name, int age, int id, float pay){// Уже лучше! Используйте свойства для установки данных класса.// Это сократит количество дублированных проверок на предмет ошибок.Name = name;Age = age;ID = id;Pay = pay;}Помимо обновления конструкторов для применения свойств при присваивании значений рекомендуется повсюду в реализации класса использовать свойства, чтобы гарантировать неизменное соблюдение бизнес-правил. Во многих случаях прямая ссылка на лежащие в основе закрытые данные производится только внутри самого свойства. Имея все сказанное в виду, модифицируйте класс
:Employeeclass Employee{<b> // Поля данных.</b>private string _empName;private int _empId;private float _currPay;private int _empAge;<b> // Конструкторы.</b>public Employee() { }public Employee(string name, int id, float pay):this(name, 0, id, pay){}public Employee(string name, int age, int id, float pay){Name = name;Age = age;ID = id;Pay = pay;}<b> // Методы.</b>public void GiveBonus(float amount) => Pay += amount;public void DisplayStats(){Console.WriteLine("Name: {0}", Name); // имя сотрудникаConsole.WriteLine("ID: {0}", Id);// идентификационный номер сотрудникаConsole.WriteLine("Age: {0}", Age); // возраст сотрудникаConsole.WriteLine("Pay: {0}", Pay); // текущая выплата}<b> // Свойства остаются прежними...</b>...}Свойства, допускающие только чтение
При инкапсуляции данных может возникнуть желание сконфигурировать свойство, допускающее только чтение, для чего нужно просто опустить блок
. Например, пусть имеется новое свойство по имениset, которое инкапсулирует закрытую строковую переменнуюSocialSecurityNumber. Вот как превратить его в свойство, доступное только для чтения:empSSNpublic string SocialSecurityNumber{get { return _empSSN; }}Свойства, которые имеют только метод
, можно упростить с использованием членов, сжатых до выражений. Следующая строка эквивалентна предыдущему блоку кода:getpublic string SocialSecurityNumber => _empSSN;Теперь предположим, что конструктор класса принимает новый параметр, который дает возможность указывать в вызывающем коде номер карточки социального страхования для объекта, представляющего сотрудника. Поскольку свойство
допускает только чтение, устанавливать значение так, как показано ниже, нельзя:SocialSecurityNumberpublic Employee(string name, int age, int id, float pay, string ssn){Name = name;Age = age;ID = id;Pay = pay;// Если свойство предназначено только для чтения, это больше невозможно!