Свойства в C#

Свойства — один из самых замечательных механизмов С# и CLR, который позволяет достичь лучшей инкапсуляции. Если говорить коротко, то свойства используются для ужесточения контроля доступа к внутреннему состоянию объекта.

С точки зрения клиента объекта свойство выглядит и ведет себя аналогично общедоступному полю. Нотация доступа к свойству такая же, как при доступе к общедоступному полю экземпляра. Однако свойство не имеет никакого ассоциированного с ним места хранения в объекте, как это присуще полям.

Вместо этого свойство представляет собой сокращенную нотацию для определения средств доступа (accessors) для чтения и записи полей. Типичный шаблон предусматривает обеспечение доступа к приватному полю класса через общедоступное свойство.

В С# 3.0 эта задача еще более облегчается за счет введения автореализуемых (auto-implemented) свойств.

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

Объект таблицы базы данных узнает, когда необходимо вычислить значение, по факту вызова клиентом средства доступа для обращения к свойству.

Объявление свойств

Синтаксис объявления свойств очень прост. Как и большинство членов класса, свойства допускают присоединение к ним атрибутов метаданных. Некоторые модификаторы, применимые к свойствам, подобны модификаторам методов. Другие модификаторы включают способность объявлять свойство как virtual, sealed, override, abstract и т.д.

В следующем коде определяется свойство Temperature в классе А:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class А
{
private int temperature;
public int Temperature
{
get
{
System.Console.WriteLine ( "Извлечение значения temperature" );
return temperature;
}
set {
System.Console.WriteLine ( "Установка значения temperature" );
temperature = value;
}
public class MainClass
{
static void Main()
{
A obj = new A () ;
obj.Temperature = 1;
System.Console.WriteLine ( "obj.Temperature = {0}", obj.Temperature );
}

Сначала определяется свойство по имени Temperature, имеющее тип int. Каждое объявление свойства должно определять тип, представляемый этим свойством. Этот тип должен быть видимым компилятору в точке, где свойство объявлено в классе, и он должен иметь как минимум ту же видимость, что и определяемое свойство.

То есть имеется в виду, что если свойство является общедоступным (public), то тип значения, представленного свойством, также должен быть объявлен как public в сборке, в которой он определен. В данном примере тип int — это псевдоним для System.Int32. Этот класс определен в пространстве имен System, и он объявлен как public. Поэтому int можно использовать в качестве типа свойства в данном общедоступном классе А.

Свойство Temperature просто возвращает приватное поле temperature из внутреннего состояния экземпляра объекта. Это — общепринятое соглашение. Приватному полю назначается имя, начинающееся с прописной буквы, а свойству — имя, начинающееся с заглавной буквы. Разумеется, следовать этому соглашению вовсе не обязательно, но нет причин от него отказываться, к тому же другие программисты С# обычно ожидают его соблюдения.

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

Средства доступа

В предыдущем примере внутри блока свойства находятся еще два блока кода. Это средства доступа (accessors) к свойству, и внутри их блоков помещается код, который читает и записывает значение свойства.

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

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

Например, если типом свойства является long, a get возвращает int, то тип int будет неявно преобразован в long без потери точности. В других отношениях код в этом блоке подобен параметризованному методу, возвращающему значение или ссылку на тип свойства.

Блок set вызывается, когда клиент пытается записывать свойство. Обратите внимание, что возвращаемого значения здесь нет.

Также обратите внимание на специальное значение по имени value, которое доступно коду внутри этого блока, и которое имеет тот же тип, что объявлен в качестве типа свойства. При записи значения в свойство переменная value устанавливается в значение или ссылку на объект, которую клиент пытается присвоить свойству.

Если вы попробуете объявить локальную переменную по имени value в блоке set, то получите ошибку компиляции. Средство доступа set подобно методу, который принимает один параметр того же типа, что у свойства, и возвращает void.

Свойства, доступные только для чтения и только для записи

Если свойство определяется только со средством доступа get, то такое свойство будет доступно только для чтения. Аналогично если свойство определяется только со средством доступа set, то получается свойство, доступное только для записи. И, наконец, свойство, имеющее оба средства доступа, допускает как чтение, так и запись.

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

Однако, учитывая тот факт, что во время JIT-компиляции CLR может встраивать код для доступа к свойству в случае, когда свойство просто возвращает поле, проблема неэффективности отпадает.

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

Фактически в 99% случаев свойство, доступное только для записи, является более гибким, чем общедоступное readonly-поле. Одна из причин связана с возможностью отложить вычисление свойства, доступного только для чтения, до того момента, когда оно понадобится (прием, известный как “ленивое” вычисление или отложенное выполнение).

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

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

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

Например, предположим, что требуется обеспечить некоторое протоколирование в отладочной сборке при каждом обращении к свойству.

Для доступа к данным клиент сможет неявно вызвать один из специальных методов свойств. Гибкость, которой можно достичь подобным образом, практически не ограничена. С другой стороны, при обращении к значению, представленному в общедоступном readonly-поле, никакой метод не вызывается, поэтому реализовать дополнительные действия можно, только перейдя от поля к свойству и перекомпилировав код.

Вы можете следить за любыми ответами на эту запись через RSS 2.0 ленту. Вы можете оставить ответ, или trackback с вашего собственного сайта.

Оставьте отзыв

XHTML: Вы можете использовать следующие теги: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

 
Rambler's Top100