public string Name
{
get { return _empName; }
set
{
// Здесь value на самом деле имеет тип string.
if (value.Length > 15)
{ Console.WriteLine("Error! Name length exceeds 15 characters!");
// Ошибка! Длина имени превышает 15 символов!
}
else
{
empName = value;
}
}
}
После определения свойств подобного рода вызывающему коду кажется, что он имеет дело с открытым элементом данных однако "за кулисами" при каждом обращении к ним вызывается корректный блок get или set, предохраняя инкапсуляцию:
Console.WriteLine("***** Fun with Encapsulation *****n");
Employee emp = new Employee("Marvin", 456, 30000);
emp.GiveBonus(1000);
emp.DisplayStats();
<b>// Переустановка и аатем получение свойства Name.</b>
emp.Name = "Marv";
Console.WriteLine("Employee is named: {0}", emp.Name); // имя сотрудника
Console.ReadLine();
Свойства (как противоположность методам доступа и изменения) также облегчают манипулирование типами, поскольку способны реагировать на внутренние операции С#. В целях иллюстрации будем считать, что тип класса Employee имеет внутреннюю закрытую переменную-член, представляющую возраст сотрудника. Ниже показаны необходимые изменения (обратите внимание на применение цепочки вызовов конструкторов):
class Employee
{
...
<b> // Новое поле и свойство.</b>
private int _empAge;
public int Age
{
get { return _empAge; }
set { _empAge = value; }
}
<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)
{
_empName = name;
_empId = id;
(window.adrunTag = window.adrunTag || []).push({v: 1, el: 'adrun-4-390', c: 4, b: 390})
_empAge = age;
_currPay = pay;
}
<b> // Обновленный метод DisplayStats() теперь учитывает возраст.</b>
public void DisplayStats()
{
Console.WriteLine("Name: {0}", _empName); // имя сотрудника
Console.WriteLine("ID: {0}", _empId);
// идентификационный номер сотрудника
Console.WriteLine("Age: {0}", _empAge); // возраст сотрудника
Console.WriteLine("Pay: {0}", _currPay); // текущая выплата
}
}
Теперь предположим, что создан объект Employee по имени joe. Необходимо сделать так, чтобы в день рождения сотрудника возраст увеличивался на 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) также могут быть обновлены соответствующей логикой.