Урок 1. Свойства. Методы доступа


#1

При создании классов всегда рекомендуется поля делать закрытыми (private), что приводит к тому, что напрямую обращаться к ним вне класса становится невозможным. Причиной же почему существует такая рекомендация стали следующие ситуации:

  • пользователь, использующий данный класс может пытаться присваивать полям недопустимые для них значения, которые, например, приводят к неправильности работы всех внутренних методов. Конечно же можно писать различные проверки в коде перед присвоением, но эти проверки понадобятся во всех местах кода, и если в дальнейшем нужно будет добавить дополнительную проверку придётся просматривать полностью весь код, что наверняка покажется очень неудобным?

  • разработчики класса, когда создавали класс, подразумевали, что для некоторых полей запись должна быть запрещена, а для некоторых наоборот, должно быть запрещено чтение значения поля, а запись разрешена, но тут возникает проблема: мы не можем ограничить доступ к открытым полям, они всегда разрешают как запись, так и считывание. Использование проверок не спасёт в этом случае, а объявления полей константами (например, public const double pi = 3.14d;) не всегда будет подходить, ведь значения в поле, возможно, нужно изменять при работе определённых методов.

Примечание: константы в классах создаются точно так же, как и обычные константы в методах, и работа с ними ничем не отличается (разве-что только возможностью применения к ним модификаторов доступа).

Тогда возникает вопрос, как организовать безопасное считывание и запись в поле класса? Многие немного подумав, дойдут к выводу, что мы можем создать отдельные методы, которые и будут отвечать за работу с полями. Соответственно для каждого поля можно создать два вот-таких метода:

string name = null;

public void SetField(string value) // Метод-мутатор - mutator  (setter)
{
     // различные проверки
     name = value;
}

publicstringGetField()           // Метод-аксессор -  accessor  (getter)
{
     // различные условия
     return name;
}

Тип параметра метода отвечающего за запись отвечает типу полю, с которым он работает, а сам же метод в качестве результата своей работы ничего не возвращает (возвращает void). Метод же отвечающий за считывание ничего не принимает, а возвращает значение, записанное в поле.

Методы, отвечающие за запись, зачастую называют методами-мутаторами - mutator (setter), а за считывание - методами-аксессорами - accessor (getter).

Теперь в дальнейшем нам ненужно будет дописывать различные проверки в коде перед присвоением/взятием значения полю, а достаточно будет поместить их в методы доступа. Но многие уже привыкли записывать значения в поля через оператор присвоения, а не через вызов метода, да и для каждого поля в классе придумывать название методов иногда может быть неудобно, поэтому запись этих методов было решено сократить до следующей формы:

private string name;

public string Field
{
     set     // void SetField(string value)   -    Метод-мутатор - mutator   (setter)
     {
          // различные проверки
          if (value != "fool")
          {
               name = value;
          }
          else
          {
               Console.WriteLine("try set fool");
          }
     }

     get     // string GetField()             -    Метод-аксессор - accessor (getter)
     {
          if (name == null)    // проверки
               return "name == null";
          else
               return name;                 
     }            
}

Таким образом:

  • тип возвращаемого значения метода-аксессора и тип параметра метода-мутатора был указан в самом начале (public string Field) + имя параметра в методе-мутаторе всегда будет value, поэтому к нему всегда можно будет обратиться.
  • названия методов были заменены двум ключевыми словами set и get соответственно.
  • кроме этого это позволило работать с полем name как и прежде через присвоение (без вызова методов), но вместо обращения к name теперь нужно будет обращаться к Field.

Также конструкция, приведённая выше является свойством из чего следует, что «Свойство» — это:

  1. способ доступа к внутреннему состоянию объекта, имитирующий переменную некоторого типа;
  2. это конструкция языка C#, которая заменяет собой использование обычных методов доступа.

Работа со свойством экземпляра напоминает работу с полями экземпляра.

Свойство состоит из имени, типа и тела. В теле задаются методы доступа, через использование ключевых слов set и get.

Метод set автоматически срабатывает тогда, когда свойству пытаются присвоить значение. Это значение представлено ключевым словом value.

Метод get автоматически срабатывает тогда, когда мы пытаемся получить значение.

Зачастую в качестве названия свойства берут название поля с которым оно работает, но первую букву пишут в верхнем регистре.

Если же посмотреть на свойство (не имеющее дополнительных проверок) через .NetReflector, то будет видно следующее:

Это говорит о том, что все свойства на самом деле являются методами.

Примечание: свойство необязательно должно быть связанно именно с полем, главное, что бы методы доступа работали правильно (соблюдены все требования к написанию метода)!


Содержание справочника С# Essential