Employee emp = new Employee("Marvin", 456, 30000);
emp.GiveBonus(1000);
emp.DisplayStats;
// Переустановка и аатем получение свойства Name.
emp.Name = "Marv";
Console.WriteLine("Employee is named: {0}", emp.Name); // имя сотрудника
Console.ReadLine;
Свойства (как противоположность методам доступа и изменения) также облегчают манипулирование типами, поскольку способны реагировать на внутренние операции С#. В целях иллюстрации будем считать, что тип
класса
Employee
имеет внутреннюю закрытую переменную-член, представляющую возраст сотрудника. Ниже показаны необходимые изменения (обратите внимание на применение цепочки вызовов конструкторов):
class Employee
{
...
// Новое поле и свойство.
private int _empAge;
public int Age
{
get { return _empAge; }
set { _empAge = value; }
}
// Обновленные конструкторы.
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)
{
_empName = name;
_empId = id;
_empAge = age;
_currPay = pay;
}
// Обновленный метод DisplayStats теперь учитывает возраст.
public void DisplayStats
{
Console.WriteLine("Name: {0}", _empName); // имя сотрудника
Console.WriteLine("ID: {0}", _empId);
// идентификационный номер сотрудника
Console.WriteLine("Age: {0}", _empAge); // возраст сотрудника
. Необходимо сделать так, чтобы в день рождения сотрудника возраст увеличивался на 1 год. Используя традиционные методы
set
и
get
, пришлось бы написать приблизительно такой код:
Employee joe = new Employee;
joe.SetAge(joe.GetAge + 1);
Тем не менее, если
empAge
инкапсулируется посредством свойства по имени
Age
, то код будет проще:
Employee joe = new Employee;
joe.Age++;
Свойства как члены, сжатые до выражений (нововведение в версии 7.0)
Как упоминалось ранее, методы
set
и
get
свойств также могут записываться в виде членов, сжатых до выражений. Правила и синтаксис те же: однострочные методы могут быть записаны с применением нового синтаксиса. Таким образом, свойство
Age
можно было бы переписать следующим образом:
public int Age
{
get => empAge;
set => empAge = value;
}
Оба
варианта кода компилируются в одинаковый набор инструкций IL, поэтому выбор используемого синтаксиса зависит только от ваших предпочтений. В книге будут сочетаться оба стиля, чтобы подчеркнуть, что мы не придерживаемся какого-то специфического стиля написания кода.
Использование свойств внутри определения класса
Свойства, в частности их порция
set
, являются общепринятым местом для размещения бизнес-правил класса. В текущий момент класс
Employee
имеет свойство
Name
, которое гарантирует, что длина имени не превышает 15 символов. Остальные свойства (
ID
,
Рау
и
Age
) также могут быть обновлены соответствующей логикой.
Хотя все это хорошо, но необходимо также принимать во внимание и то, что обычно происходит внутри конструктора класса. Конструктор получает входные параметры, проверяет данные на предмет допустимости и затем присваивает значения внутренним закрытым полям. Пока что главный конструктор не проверяет входные строковые данные на вхождение в диапазон допустимых значений, а потому его можно было бы изменить следующим образом:
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;
}
Наверняка вы заметили проблему, связанную с таким подходом. Свойство
Name
и главный конструктор выполняют одну и ту же проверку на наличие ошибок. Реализуя проверки для других элементов данных, есть реальный шанс столкнуться с дублированием кода. Стремясь рационализировать код и изолировать всю проверку, касающуюся ошибок, в каком-то центральном местоположении, вы добьетесь успеха, если для получения и установки значений внутри класса всегда будете применять свойства. Взгляните на показанный ниже модифицированный конструктор:
public Employee(string name, int age, int id, float pay)
{
// Уже лучше! Используйте свойства для установки данных класса.
// Это сократит количество дублированных проверок на предмет ошибок.
Name = name;
Age = age;
ID = id;
Pay = pay;
}
Помимо обновления конструкторов для применения свойств при присваивании значений рекомендуется повсюду в реализации класса использовать свойства, чтобы гарантировать неизменное соблюдение бизнес-правил. Во многих случаях прямая ссылка на лежащие в основе закрытые данные производится только внутри самого свойства. Имея все сказанное в виду, модифицируйте класс