Инкапсуляция на основе свойств класса

Инкапсуляция на основе свойств класса

В отличие от традиционных методов чтения и модификации, языки .NET тяготеют к реализации принципа инкапсуляции на основе использования свойств, которые представляют доступные для внешнего пользователя элементы данных. Вместо того, чтобы вызывать два разных метода (get и set) для чтения и установки данных состояния объекта, пользователь получает возможность вызвать нечто, похожее на общедоступное поле. Предположим, что мы имеем свойство с именем ID (код), представляющее внутренний член-переменную empID типа Employee. Синтаксис вызова в данном случае должен выглядеть примерно так.

// Синтаксис установки/чтения значения ID работника.

static void Main(string[] args) {

 Employee p = new Employee();

 // Установка значения.

 p.ID = 81;

 // Чтение значения.

 Console.WriteLine ("ID работника: {0} ", p.ID); Console.ReadLine();

}

Свойства типа "за кадром" всегда отображаются в "настоящие" методы чтения и модификации. Поэтому, как разработчик класса, вы имеете возможность реализовать любую внутреннюю логику, выполняемую перед присваиванием соответствующего значения (например, перевод символов в верхний регистр, очистку значения от недопустимых символов, проверку принадлежности числового значения диапазону допустимости и т.д.). Ниже демонстрируется синтаксис C#. использующий, кроме свойства ID, свойство Pay (оплата), которое инкапсулирует поле currPay, a также свойство Name (имя), которое инкапсулирует данные fullName.

// Инкапсуляция с помощью свойств.

public class Employee {

 ...

 private int empID;

 private float currPay;

 private string fullName;

 // Свойство для empID.

 public int ID {

  get {return empID;}

  set {

   // Вы можете проверить и, если требуется, модифицировать

   // поступившее значение перед присваиванием.

   empID = value;

  }

 }

 // Свойство для fullName.

 public string Name {

  get { return fullName; }

  set { fullName = value; }

 }

 // Свойство для currPay.

 public float Pay {

  get { return currPay; }

  set { currPay = value; }

 }

}

Свойство в C# компонуется из блока чтения и блока модификации (установки) значении. Ярлык value в C# представляет правую сторону оператора присваивания. Соответствующий ярлыку value тип данных зависит от того, какого сорта данные этот ярлык представляет. В данном примере свойство ID оперирует с типом данных int, который, как вы знаете, отображается в System.Int32.

// 81 принадлежит System.Int32,

// поэтому "значением" является System.Int32.

Employee e = new Employee();

e.ID = 81;

Чтобы это доказать, предположим, что вы добавили следующий программный код для установки свойства ID.

// Свойство для empID.

public int ID {

 get {return empID;}

 set {

  Console.WriteLine("value является экземпляром {0} ", value.GetType());

  Console.WriteLine("Значение value: {0} ", value); empID = value;

 }

}

Выполнив приложение, вы должны увидеть вариант вывода, показанный на рис. 4.5.

Рис. 4.5. Значение value после установки для ID значения 81

Замечание. Строго говоря, ярлык value в C# является не ключевым оловом, а, скорее, контекстным ключевым словом, представляющим неявный параметр, который используется в операторе присваивания в контексте метода, используемого для установки значения свойства. Поэтому вполне допустимо иметь члены-переменные и локальные элементы данных с именем value.

Следует понимать, что свойства (в отличие от традиционных методов чтения и модификации) еще и упрощают работу с типами, поскольку свойства способны "реагировать" на внутренние операции в C#. Например, предположим, что тип класса Employee имеет внутренний приватный член, представляющий значение возраста работника. Вот соответствующая модификация класса.

public class Employee {

 …

 // Текущий возраст работника.

 private int empAge;

 public Employee(string fullName, int age, int empID, float currPay) {

  …

  this.empAge = age;

 }

 public int Age {

  get { return empAge; }

  set { empAge = value; }

 }

 public void DisplayStats() {

  …

  Console.WriteLine("Возраст: {0} ", empAge);

 }

}

Теперь предположим, что вы создали объект Employee с именем joe. Вы хотите, чтобы в день рождения работника значение переменной возраста увеличивалось на единицу. Используя традиционные методы чтения и модификации, вы должны применить, например, следующий программный код.

Employee joe = new Employee();

joe.SetAge(joe.GetAge() + 1);

Но если инкапсулировать empAge, используя "правильный" синтаксис, вы сможете просто написать:

Employee joe = new Employee();

joe.Age++;