Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
CSharp Language Specification.doc
Скачиваний:
13
Добавлен:
26.09.2019
Размер:
4.75 Mб
Скачать

10.7.1Статические свойства и свойства экземпляра

Если объявление свойства включает модификатор static, свойство называется статическим свойством. Если модификатор static отсутствует, свойство называется свойством экземпляра.

Статическое свойство не связано с конкретным экземпляром. При использовании зарезервированного слова this в методах доступа статического свойства возникает ошибка времени компиляции.

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

При доступе_к_члену (§7.6.4) с использованием ссылки на свойство в форме E.M для статического свойства M параметр E должен означать тип, содержащий M, а для свойства экземпляра M — экземпляр типа, содержащего M.

Различия между статическими членами и членами экземпляров рассматриваются в разделе §10.3.7.

10.7.2Методы доступа

Объявления_методов_доступа свойства задают исполняемые операторы, связанные с чтением или записью свойства.

объявления_методов_доступа: объявление_метода_доступа_get объявление_метода_доступа_setнеобязательно объявление_метода_доступа_set объявление_метода_доступа_getнеобязательно

объявление_метода_доступа_get: атрибутынеобязательно модификатор_метода_доступанеобязательно get тело_метода_доступа

объявление_метода_доступа_set: атрибутынеобязательно модификатор_метода_доступанеобязательно set тело_метода_доступа

модификатор_метода_доступа: protected internal private protected internal internal protected

тело_метода_доступа: блок ;

Объявление метода доступа включает в себя объявление_метода_доступа_get, объявление_метода_доступа_set или оба этих объявления. Каждое объявление метода доступа состоит из маркера get или set, за которым следуют необязательный модификатор_метода_доступа и тело_метода_доступа.

На использование модификаторов_методов_доступа налагаются следующие ограничения.

  • Модификатор_метода_доступа не может использоваться в интерфейсе или явной реализации члена интерфейса.

  • Для свойства или индексатора, не имеющего модификатора override, модификатор_метода_доступа может использоваться только в том случае, если свойство или индексатор содержит оба метода доступа (get и set), и применяется только к одному из них.

  • Для свойства или индексатора, содержащего модификатор override, метод доступа должен соответствовать используемому модификатору_метода_доступа переопределяемого метода доступа.

  • Модификатор_метода_доступа должен объявлять более строгий уровень доступа, чем уровень доступа самого свойства или индексатора. Более точно:

  • Если свойство или индексатор имеют объявленный уровень доступа public, модификатор_метода_доступа может быть любым из указанных: protected internal, internal, protected или private.

  • Если для свойства или индексатора объявлен уровень доступа protected internal, можно использовать модификатор_метода_доступа с уровнем доступа internal, protected или private.

  • Если для свойства или индексатора объявлен уровень доступа internal или protected, можно использовать модификатор_метода_доступа с уровнем доступа private.

  • Если для свойства или индексатора объявлен уровень доступа private, использовать модификаторы_метода_доступа нельзя.

Для свойств с модификатором abstract и extern тело_метода_доступа содержит только точку с запятой. Свойства, не являющиеся абстрактными или внешними, могут быть автоматически реализуемыми свойствами. В этом случае задаются оба метода доступа (get и set), в теле которых содержится только точка с запятой (§10.7.3). Для методов доступа любого свойства, за исключением свойств, не являющихся абстрактными или внешними, тело_метода_доступа является блоком, который задает операторы, исполняемые при вызове соответствующего метода доступа.

Метод доступа get соответствует не содержащему параметров методу, возвращаемое значение которого имеет тип свойства. За исключением случаев, когда свойство является конечным объектом операции присваивания, при ссылке на свойство в выражении вызывается метод доступа get для вычисления значения свойства (§7.1.1). Тело метода доступа get должно соответствовать правилам для возвращающих значение методов, описанным в §10.6.10. В частности, все операторы return в теле метода доступа get должны задавать выражение, которое может быть неявно преобразовано к типу свойства. Более того, конечная точка метода доступа get не должна достигаться.

Метод доступа set соответствует методу с типом возвращаемого значения void и одним параметром значения, имеющим тип свойства. Метод доступа set всегда имеет неявный параметр value. Если свойство является конечным объектом операции присваивания (§7.17) или операндом операторов «++» или «--» (§7.6.9, §7.7.5), метод доступа set вызывается с аргументом (значение аргумента располагается в правой части операции присваивания или является операндом операторов «++» или «--»), который предоставляет новое значение (§7.17.1). Тело метода доступа set должно соответствовать правилам для возвращающих void методов, описанным в §10.6.10. В частности, операторам return в теле метода доступа set не разрешается задавать выражение. Объявление локальной переменной или константы в методе доступа set, имеющее имя value, является ошибкой времени компиляции, поскольку метод доступа set неявно имеет параметр с таким именем.

В зависимости от наличия или отсутствия методов доступа get и set свойства классифицируются следующим образом.

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

  • Свойство, содержащее только метод доступа get, называется свойством только для чтения. Адресатом назначения является ошибка во время компиляции для свойства только для чтения.

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

В примере

public class Button: Control { private string caption;

public string Caption { get { return caption; } set { if (caption != value) { caption = value; Repaint(); } } }

public override void Paint(Graphics g, Rectangle r) { // Painting code goes here } }

элемент управления Button объявляет открытое свойство Caption. Метод доступа get свойства Caption возвращает строку, хранящуюся в закрытом поле caption. Метод доступа set сравнивает новое значение с текущим, и если оно отличается, сохраняет новое значение и обновляет элемент управления. Для свойств часто используется описанный выше шаблон. Метод доступа get просто возвращает значение, хранящееся в закрытом поле, а метод set изменяет закрытое поле и выполняет определенные действия, необходимые для полного обновления состояния объекта.

В следующем примере показано использование свойства Caption для описанного выше класса Button.

Button okButton = new Button(); okButton.Caption = "OK"; // Invokes set accessor string s = okButton.Caption; // Invokes get accessor

В этом примере метод доступа set вызывается посредством присваивания значения свойству, а метод доступа get — посредством ссылки на свойство в выражении.

Методы доступа get и set свойства не являются отдельными членами и не могут объявляться отдельно от свойства. Два метода доступа на чтение и запись не могут иметь различные уровни доступа. Пример.

class A { private string name;

public string Name { // Error, duplicate member name get { return name; } }

public string Name { // Error, duplicate member name set { name = value; } } }

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

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

class A { public int P { set {...} } }

class B: A { new public int P { get {...} } }

свойство P класса B скрывает свойство P класса A по отношению к операциям чтения и записи. Таким образом, в операторах

B b = new B(); b.P = 1; // Error, B.P is read-only ((A)b).P = 1; // Ok, reference to A.P

присваивание свойству b.P вызывает ошибку времени компиляции, поскольку свойство только для чтения P класса B скрывает свойство только для записи P класса A. Обратите внимание, что для доступа к скрытому свойству P можно использовать приведение.

В отличие от открытых полей свойства обеспечивают разделение между внутренним состоянием объекта и его открытым интерфейсом. Рассмотрим пример.

class Label { private int x, y; private string caption;

public Label(int x, int y, string caption) { this.x = x; this.y = y; this.caption = caption; }

public int X { get { return x; } }

public int Y { get { return y; } }

public Point Location { get { return new Point(x, y); } }

public string Caption { get { return caption; } } }

В этом примере в классе Label используется два поля типа int (x и y) для хранения местоположения надписи. Сведения о местоположении предоставляются открытым образом в виде свойств X и Y, а также свойства Location типа Point. Если в последующих версиях класса Label потребуется внутреннее хранение данных о местоположении с помощью типа Point, это можно реализовать, не внося изменения в открытый интерфейс класса:

class Label { private Point location; private string caption;

public Label(int x, int y, string caption) { this.location = new Point(x, y); this.caption = caption; }

public int X { get { return location.x; } }

public int Y { get { return location.y; } }

public Point Location { get { return location; } }

public string Caption { get { return caption; } } }

Если бы свойства x и y были полями public только для чтения, внести такие изменения в класс Label было бы невозможно.

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

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

class Counter { private int next;

public int Next { get { return next++; } } }

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

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

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

using System.IO;

public class Console { private static TextReader reader; private static TextWriter writer; private static TextWriter error;

public static TextReader In { get { if (reader == null) { reader = new StreamReader(Console.OpenStandardInput()); } return reader; } }

public static TextWriter Out { get { if (writer == null) { writer = new StreamWriter(Console.OpenStandardOutput()); } return writer; } }

public static TextWriter Error { get { if (error == null) { error = new StreamWriter(Console.OpenStandardError()); } return error; } } }

Класс Console содержит три свойства (In, Out и Error), которые представляют стандартные устройства ввода, вывода и вывода ошибок соответственно. Благодаря предоставлению этих членов в виде свойств класса Console обеспечивается задержка их инициализации до момента фактического использования Например, при первой ссылке на свойство Out, как показано в примере

Console.Out.WriteLine("hello, world");

создается базовый объект TextWriter для устройства вывода. Однако если приложение не ссылается на свойства In и Error, объекты для этих устройств не создаются.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]