Шаг 120.
Язык программирования C#. Начала
Свойства и индексаторы. Знакомство со свойствами

    На этом шаге мы рассмотрим назначение и общую схему использования свойств.

    Мы начинаем знакомство со свойствами. Свойство - это член класса, как поле или метод. Но это и не поле, и не метод. Свойство в языке C# - это нечто среднее между полем и методом. Если исходить из того, как свойство используется, то оно, конечно, очень напоминает поле. Но принципы реализации свойства больше напоминают описание методов. Чтобы было легче понять, как свойство описывается в классе, начнем с того, как оно используется.

    Используется свойство, как отмечалось выше, практически так же, как поле. Для большей конкретики представим, что существует некий объект и у этого объекта есть поле (например, числовое). Что мы можем делать с этим полем? В принципе, есть две базовые операции:

И в том, и в другом случае мы указываем имя объекта и название поля (через точку). Реакция на такую инструкцию достаточно простая: выполняется обращение к области памяти, в которой хранится значение поля. Если в команде значение поля считывается, то это значение будет прочитано в соответствующей области памяти. Если значение полю присваивается, то значение будет записано в соответствующую область памяти. Подчеркнем, что все это для поля. В случае со свойством формально все выглядит очень похоже. Как и у поля, у свойства есть тип и есть название. Если мы хотим узнать значение свойства, то действуем подобно случаю с полем: после имени объекта через точку указывается название свойства. Если мы хотим присвоить значение свойству, то указываем имя объекта, название свойства (через точку) и, после оператора присваивания, присваиваемое значение. То есть все, как и в случае с полем. В чем же разница? А разница в действиях, которые выполняются при считывании значения свойства и при присваивании значения свойству.

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


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

    Описывается свойство немного сложнее, чем поле (оно и понятно - в общем случае со свойством связано два метода-аксессора, которые также нужно описать при описании свойства). Вначале указывается тип и название свойства (как и при описании поля), затем в блоке в фигурных скобках описываются методы-аксессоры. Эти методы (их обычно два) описываются специальным образом.


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

    Метод, вызываемый при считывании значения свойства (get-аксессор), описывается следующим образом: указывается ключевое слово get, после которого в фигурных скобках описываются команды, выполняемые при считывании значения свойства. То есть никаких формальных атрибутов метода (тип результата, название, список аргументов) здесь нет. Объяснение простое. Уникальное название методу не нужно, поскольку метод "самостоятельной ценности" не имеет и вызывается при обращении к свойству (то есть имя свойства "замещает" имя метода). Аргументы методу не нужны по определению, поскольку при считывании значения свойства никакие дополнительные значения или атрибуты не используются. А вот результат метод возвращает (хотя идентификатор типа результата явно никак не обозначен). Возвращаемый get-аксессором результат - это значение свойства. Поэтому тип результата get-акссссора совпадает с типом свойства.

    Метод, который вызывается при присваивании значения свойству (set-аксессор), описывается таким образом: указывается ключевое слово set, а в фигурных скобках описываются команды, выполняемые при присваивании значения свойству. Данный метод-аксессор не возвращает результат, и у него нет аргументов. Но один параметр методу все же нужен: это значение, которое присваивается свойству. Поскольку аргумента у метода нет, то аргументом данное значение в метод передать нельзя. Поэтому присваиваемое свойству значение в set-аксессор передается с помощью ключевого слова value. Это ключевое слово, которое в блоке описания set-аксессора отождествляется со значением, присваиваемым свойству.

    Ниже представлен шаблон, в соответствии с которым описывается свойство:

тип имя {
  get {
      // Код get-аксессора
  }
  set {
      // Код set-аксессора
  }
}

    При описании свойства можно определить не два, а только один аксессор: или get-аксессор, или set-аксессор. Если для свойства определен только get-аксессор, то значение такого свойства можно прочитать, но такому свойству нельзя присвоить значение. Если для свойства определен только set-аксессор, то значение свойству можно присвоить, но нельзя его прочитать.


Далее мы рассмотрим разные способы определения свойств, включая и свойства с одним аксессором. Пока же заметим, что примером свойства, доступного только для чтения, но не доступного для присваивания, может быть свойство Length у массивов и текстовых строк.

    Еще одна важная особенность свойства состоит в том, что под свойство место в памяти не выделяется. То есть из того факта, что мы описали свойство, не следует, что при создании объекта под это свойство выделяется память. В этом смысле свойство принципиально отличается от поля. Поэтому если мы собираемся при работе со свойством запоминать и использовать некоторое значение, то нам придется для этого специально описать поле (обычно закрытое). Фактически свойство - это некая "ширма", за которой может "скрываться" все, что угодно.


Поскольку свойство не определяет область памяти и не связано с областью памяти, то свойство нельзя использовать с идентификаторами ref и out.

    На следующем шаге мы закончим изучение этого вопроса.




Предыдущий шаг Содержание Следующий шаг