Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Индексаторы и свойства.docx
Скачиваний:
2
Добавлен:
01.07.2025
Размер:
82 Кб
Скачать
☆

10.2 Свойства

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

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

Ниже приведена общая форма свойства:

тип имя {

get

{

// код аксессора для чтения из поля

}

set

{

// код аксессора для записи в поле

}

}

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

Следует, однако, иметь в виду, что свойства не определяют место в памяти для хра­нения полей, а лишь управляют доступом к полям. Это означает, что само свойство не предоставляет поле, и поэтому поле должно быть определено независимо от свойства. (Исключение из этого правила составляет автоматически реализуемое свойство, рассма­триваемое далее.)

Ниже приведен простой пример программы, в которой определяется свойство MyProp, предназначенное для доступа к полю prop. В данном примере свойство до­пускает присваивание только положительных значений.

Листинг 10.5

// Простой пример применения свойства

using System;

class SimpProp

{

int prop; // поле, управляемое свойством MyProp

public SimpProp() { prop = 0; }

/* Это свойство обеспечивает доступ к закрытой переменной

экземпляра prop. Оно допускает присваивание только

положительных значений. */

public int MyProp

{

get {

return prop;

}

set {

if(value >= 0) prop = value;

}

}

}

// Продемонстрировать применение свойства

class PropertyDemo

{

static void Main()

{

SimpProp ob = new SimpProp();

Console.WriteLine("Первоначальное значение ob.MyProp: " +

ob.MyProp);

ob.MyProp = 100; // Присвоить значение

Console.WriteLine("Текущее значение ob.MyProp: " + ob.MyProp);

// Переменной prop нельзя прсвоить отрицательное значение

Console.WriteLine(“Попытка присвоить значение “

+ “-10 свойству ob.MyProp”);

ob.MyProp = -10;

Console.WriteLine("Текущее значение ob.MyProp: " + ob.MyProp);

}

}

Вот к какому результату приводит выполнение этого кода.

Первоначальное значение ob.MyProp: 0

Текущее значение ob.MyProp: 100

Попытка присвоить значение -10 свойству ob.MyProp

Текущее значение ob.MyProp: 100

Рассмотрим приведенный выше код более подробно. В этом коде определяется одно закрытое поле prop и свойство МуРrор, управляющее доступом к полю prop. Как пояснялось выше, само свойство не определяет место в памяти для хранения поля, а только управляет доступом к полю. Кроме того, поле prop является закрытым, а зна­чит, оно доступно только через свойство МуРrор.

Свойство МуРrор указано как public, а следовательно, оно доступно из кода за пределами его класса. И в этом есть своя логика, поскольку данное свойство обеспе­чивает доступ к полю prop, которое является закрытым. Аксессор get этого свойства просто возвращает значение из поля prop, тогда как аксессор set устанавливает зна­чение в поле prop в том и только в том случае, если это значение оказывается поло­жительным. Таким образом, свойство МуРrор контролирует значения, которые могут храниться в поле prop. В этом, собственно, и состоит основное назначение свойств.

Тип свойства МуРrор определяется как для чтения, так и для записи, поскольку оно позволяет читать и записывать данные в базовое поле. Тем не менее свойства можно создавать доступными только для чтения или только для записи. Так, если требуется создать свойство, доступное только для чтения, то достаточно определить единствен­ный аксессор get. А если нужно создать свойство, доступное только для записи, то достаточно определить единственный аксессор set.

Воспользуемся свойством для дальнейшего усовершенствования отказоустойчивого массива. Как вам должно быть уже известно, у всех массивов имеется соответствую­щее свойство длины (Length). До сих пор в классе FailSoftArray для этой цели ис­пользовалось открытое целочисленное поле Length. Но это далеко не самый лучший подход, поскольку он допускает установку значений, отличающихся от длины отказоу­стойчивого массива. (Например, программист, преследующий злонамеренные цели, может умышленно ввести неверное значение в данном поле.) Для того чтобы испра­вить это положение, превратим поле Length в свойство "только для чтения", как по­казано в приведенном ниже, измененном варианте класса FailSoftArray.

Листинг 10.6

// Добавить свойство Length в класс FailSoftArray

using System;

class FailSoftArray

{

int[] a; // Ссылка на базовый массив

int len; // Длина массива – служит основанием для свойства Lenght

public bool ErrFlag; // Обозначает результат последней операции

// Построить массив заданного размера

public FailSoftArray(int size)

{

a = new int[size];

len = size;

}

// Свойство Length только для чтения

public int Length {

get {

return len;

}

}

// Этот индексатор для класса FailSoftArray.

public int this[int index]

{

// Это аксессор get

get

{

if(ok(index)) {

ErrFlag = false;

return a[index];

} else {

ErrFlag = true;

return 0;

}

}

// Это аксессор set

set

{

if(ok(index)) {

a[index] = value;

ErrFlag = false;

}

else ErrFlag = true;

}

}

// Возвратить логическое значение true, если

// индекс находится в установленных границах

private bool ok(int index)

{

if(index >= 0 & index < Length) return true;

return false;

}

}

// Продемонстрировать применение усовершенствованного

// отказоустойчивго массива

class ImprovedFSDemo

{

static void Main()

{

FailSoftArray fs = new FailSoftArray(5);

int x;

// Разрешить чтение свойства Length

for(int i=0; i < fs.Length; i++)

fs[i] = i*10;

for(int i=0; i < fs.Length; i++) {

x = fs[i];

if(x != -1) Console.Write(x + " ");

}

Console.WriteLine();

// fs.Length = 10; // Ошибка, запись запрещена!

}

}

Теперь Length  это свойство, в котором местом для хранения данных служит закрытая переменная 1еп. А поскольку в этом свойстве определен единственный аксессор get, то оно доступно только для чтения. Это означает, что значение свойства Length можно только читать, но не изменять. Для того чтобы убедиться в этом, по­пробуйте При попытке скомпилировать данный код вы получите сообщение об ошибке, уведомляющее о том, что Length является свойством, доступным только для чтения.

Добавлением свойства Length в класс FailSoftArray усовершенствование рас­сматриваемого здесь примера кода с помощью свойств далеко не исчерпывается. Еще одним членом данного класса, подходящим для превращения в свойство, служит пере­менная ErrFlag, поскольку ее применение должно быть ограничено только чтением. Ниже приведен окончательно усовершенствованный вариант класса FailSoftArray, в котором создается свойство Error, использующее в качестве места для хранения дан­ных исходную переменную ErrFlag, ставшую теперь закрытой.

Листинг 10.7

// Превратить переменную ErrFlag в свойство

using System;

class FailSoftArray

{

int[] a; // ссылка на базовый массив

int len; // длина массива

bool ErrFlag; // Теперь это закрытая переменная,

// обозначающая результат последней операции

// Построить массив заданного размера

public FailSoftArray(int size)

{

a = new int[size];

len = size;

}

// Свойство Length только для чтения

public int Length

{

get

{

return len;

}

}

// Свойство Error только для чтения

public bool Error

{

get

{

return ErrFlag;

}

}

// Этот индексатор для класса FailSoftArray

public int this[int index]

{

// Это аксессор get

get

{

if(ok(index)) {

ErrFlag = false;

return a[index];

}

else

{

ErrFlag = true;

return 0;

}

}

// Это аксессор set

set

{

if(ok(index))

{

a[index] = value;

ErrFlag = false;

}

else ErrFlag = true;

}

}

// Возвратить логическое значение true, если

// индекс находится в установленных границах

private bool ok(int index)

{

if(index >= 0 & index < Length) return true;

return false;

}

}

// Продемонстрировать применение отказоустойчивго массива

class FinalFSDemo

{

static void Main()

{

FailSoftArray fs = new FailSoftArray(5);

// Использовать свойство Error

for(int i=0; i < fs.Length + 1; i++) {

fs[i] = i*10;

if(fs.Error)

Console.WriteLine("Ошибка в индексе " + i);

}

}

}

Создание свойства Error стало причиной двух следующих изменений в классе FailSoftArray. Во-первых, переменная ErrFlag была сделана закрытой, посколь­ку теперь она служит базовым местом хранения данных для свойства Error, а следо­вательно, она не должна быть доступна непосредственно. И во-вторых, было введено свойство Error "только для чтения". Теперь свойство Error будет опрашиваться в тех программах, где требуется организовать обнаружение ошибок. Именно это и было продемонстрировано выше в методе Main(), где намеренно сгенерирована ошибка нарушения границ массива, а для ее обнаружения использовано свойство Error.