Полное руководство. С# 4.0 - Шилдт Герберт
Шрифт:
Интервал:
Закладка:
Создание свойства Error стало причиной двух следующих изменений в классеFailSoftArray. Во-первых, переменная ErrFlag была сделана закрытой, поскольку теперь она служит базовым местом хранения данных для свойства Error, а следовательно, она не должна быть доступна непосредственно. И во-вторых, было введеносвойство Error "только для чтения". Теперь свойство Error будет опрашиваться в техпрограммах, где требуется организовать обнаружение ошибок. Именно это и былопродемонстрировано выше в методе Main(), где намеренно сгенерирована ошибканарушения границ массива, а для ее обнаружения использовано свойство Error.Автоматически реализуемые свойства
Начиная с версии C# 3.0, появилась возможность для реализации очень простыхсвойств, не прибегая к явному определению переменной, которой управляет свойство.Вместо этого базовую переменную для свойства автоматически предоставляет компилятор. Такое свойство называется автоматически реализуемым и принимает следующую общую форму:тип имя { get; set; }
где тип обозначает конкретный тип свойства, а имя — присваиваемое свойству имя.Обратите внимание на то, что после обозначений аксессоров get и set сразу же следует точка с запятой, а тело у них отсутствует. Такой синтаксис предписывает компилятору создать автоматически переменную, иногда еще называемую поддерживающим полем, для хранения значения. Такая переменная недоступна непосредственно и не имеетимени. Но в то же время она может быть доступна через свойство.
Ниже приведен пример объявления свойства, автоматически реализуемого подименем UserCount.public int UserCount { get; set; }
Как видите, в этой строке кода переменная явно не объявляется. И как пояснялосьвыше, компилятор автоматически создает анонимное поле, в котором хранится значение. А в остальном автоматически реализуемое свойство UserCount подобно всемостальным свойствам.
Но в отличие от обычных свойств автоматически реализуемое свойство не можетбыть доступным только для чтения или только для записи. При объявлении этого свойства в любом случае необходимо указывать оба аксессора — get и set. Хотя добитьсяжелаемого (т.е. сделать автоматически реализуемое свойство доступным только длячтения или только для записи) все же можно, объявив ненужный аксессор как private(подробнее об этом — в разделе "Применение модификаторов доступа в аксессорах").
Несмотря на очевидные удобства автоматически реализуемых свойств, их применение ограничивается в основном теми ситуациями, в которых не требуется управлениеустановкой или получением значений из поддерживающих полей. Напомним, чтоподдерживающее поле недоступно напрямую. Это означает, что на значение, котороеможет иметь автоматически реализуемое свойство, нельзя наложить никаких ограничений. Следовательно, имена автоматически реализуемых свойств просто заменяютсобой имена самих полей, а зачастую именно это и требуется в программе. Автоматически реализуемые свойства могут оказаться полезными и в тех случаях, когда с помощью свойств функциональные возможности программы открываются для стороннихпользователей, и для этой цели могут даже применяться специальные средства проектирования.Применение инициализаторов объектов в свойствах
Как пояснялось в главе 8, инициализатор объекта применяется в качестве альтернативы явному вызову конструктора при создании объекта. С помощью инициализаторов объектов задаются начальные значения полей или свойств, которые требуется инициализировать. При этом синтаксис инициализаторов объектов оказывается одинаковым как для свойств, так и для полей. В качестве примера ниже приведена программаиз главы 8, измененная с целью продемонстрировать применение инициализаторовобъектов в свойствах. Напомним, что в версии этой программы из главы 8 использовались поля, а приведенная ниже версия отличается лишь тем, что в ней поля Countи Str превращены в свойства. В то же время синтаксис инициализаторов объектов неизменился.// Применить инициализаторы объектов в свойствах.using System;class MyClass { // Теперь это свойства. public int Count { get; set; } public string Str { get; set; }}class ObjInitDemo { static void Main() { // Сконструировать объект типа MyClass с помощью инициализаторов объектов. MyClass obj = new MyClass { Count = 100, Str = "Тестирование" }; Console.WriteLine(obj.Count + " " + obj.Str); }}
Как видите, свойства Count и Str устанавливаются в выражениях с инициализатором объекта. Приведенная выше программа дает такой же результат, как и программаиз главы 8, а именно:100 Тестирование
Как пояснялось в главе 8, синтаксис инициализатора объекта оказывается наиболеепригодным для работы с анонимными типами, формируемыми в LINQ-выражениях.А в остальных случаях чаще всего используется синтаксис обычных конструкторов.Ограничения, присущие свойствам
Свойствам присущ ряд существенных ограничений. Во-первых, свойство не определяет место для хранения данных, и поэтому не может быть передано методу в качествепараметра ref или out. Во-вторых, свойство не подлежит перегрузке. Наличие двухразных свойств с доступом к одной и той же переменной допускается, но это, скорее,исключение, чем правило. И наконец, свойство не должно изменять состояние базовой переменной при вызове аксессора get. И хотя это ограничительное правило несоблюдается компилятором, его нарушение считается семантической ошибкой. Действие аксессора get не должно носить характер вмешательства в функционированиепеременной.Применение модификаторов доступа в аксессорах
По умолчанию доступность аксессоров set и get оказывается такой же, как и уиндексатора и свойства, частью которых они являются. Так, если свойство объявляетсякак public, то по умолчанию его аксессоры set и get также становятся открытыми(public). Тем не менее для аксессора set или get можно указать собственный модификатор доступа, например private. Но в любом случае доступность аксессора,определяемая таким модификатором, должна быть более ограниченной, чем доступность, указываемая для его свойства или индексатора.
Существует целый ряд причин, по которым требуется ограничить доступность аксессора. Допустим, что требуется предоставить свободный доступ к значению свойства,но вместе с тем дать возможность устанавливать это свойство только членам его класса.Для этого достаточно объявить аксессор данного свойства как private. В приведенном ниже примере используется свойство MyProp, аксессор set которого указан какprivate.// Применить модификатор доступа в аксессоре.using System;class PropAccess { int prop; // поле, управляемое свойством МуРrор public PropAccess() { prop = 0; } /* Это свойство обеспечивает доступ к закрытой переменной экземпляра prop. Оно разрешает получать значение переменной prop из любого кода, но устанавливать его — только членам своего класса. */ public int МуРrор { get { return prop; } private set { // теперь это закрытый аксессор prop = value; } } // Этот член класса инкрементирует значение свойства МуРrор. public void IncrProp() { MyProp++; // Допускается в. том же самом классе. }}// Продемонстрировать применение модификатора доступа в аксессоре свойства.class PropAccessDemo { static void Main() { PropAccess ob = new PropAccess(); Console.WriteLine("Первоначальное значение ob.МуРrор: " + ob.МуРrор); // ob.МуРrор = 100; // недоступно для установки ob.IncrProp(); Console.WriteLine("Значение ob.МуРrор после инкрементирования: " + ob.МуРrор); }}
В классе PropAccess аксессор set указан как private. Это означает, что он доступен только другим членам данного класса, например методу IncrProp(), но недоступен для кода за пределами класса PropAccess. Именно поэтому попытка Присвоитьсвойству ob.МуРrор значение в классе PropAccessDemo закомментирована.
Вероятно, ограничение доступа к аксессорам оказывается наиболее важным дляработы с автоматически реализуемыми свойствами. Как пояснялось выше, создатьавтоматически реализуемое свойство только для чтения или же только для записинельзя, поскольку оба аксессора, get и set, должны быть указаны при объявлениитакого свойства. Тем не менее добиться желаемого результата все же можно, объявиводин из аксессоров автоматически реализуемого свойства как private. В качествепримера ниже приведено объявление автоматически реализуемого свойства Lengthдля класса FailSoftArray, которое фактически становится доступным только длячтения.public int Length { get; private set; }