Чтение онлайн

ЖАНРЫ

Интернет-журнал "Домашняя лаборатория", 2007 №9
Шрифт:

public class Person

{

// поля (все закрыты)

string fam="", status="", health="";

int age=0, salary=0;

//методы — свойства

/// <summary>

/// стратегия: Read,Write-once (Чтение, запись при

/// первом обращении)

/// </summary>

public string Fam

{

set {if (fam == "") fam = value;}

get {return(fam);}

}

/// <summary>

///стратегия: Read-only(Только чтение)

/// </summary>

public string Status

{

get {return(status);}

}

/// <summary>

/// стратегия: Read,Write (Чтение, запись)

/// </summary> public int Age

{

set

{

age = value;

if(age < 7)status ="ребенок";

else if(age <17)status ="школьник";

else if (age < 22)status = "студент";

else status = "служащий";

}

get {return(age);}

}

/// <summary>

///

стратегия: Write-only (Только запись)

/// </summary>

public int Salary

{

set {salary = value;}

}

}

Рассмотрим теперь общий синтаксис методов-свойств. Пусть name — это закрытое свойство. Тогда для него можно определить открытый метод-свойство (функцию), возвращающую тот же тип, что и поле name. Имя метода обычно близко к имени поля (например, Name). Тело свойства содержит два метода — get и set, один из которых может быть опущен. Метод get возвращает значение закрытого поля, метод set — устанавливает значение, используя передаваемое ему значение в момент вызова, хранящееся в служебной переменной со стандартным именем value. Поскольку get и set — это обычные процедуры языка, то программно можно реализовать сколь угодно сложные стратегии доступа. В нашем примере фамилия меняется, только если ее значение равно пустой строке и это означает, что фамилия персоны ни разу еще не задавалась. Статус персоны пересчитывается автоматически при всяком изменении возраста, явно изменять его нельзя. Вот пример, показывающий, как некоторый клиент создает и работает с полями персоны:

public void TestPersonProps

{

Person pers1 = new Person;

pers1.Fam = "Петров";

pers1.Age = 21;

pers1.Salary = 1000;

Console.WriteLine ("Фам={0}, возраст={1}, статус={2}",

pers1.Fam, pers1.Age, persl.Status);

pers1.Fam = "Иванов"; pers1.Age += 1;

Console.WriteLine ("Фам={0}, возраст={1}, статус={2}",

pers1.Fam, pers1.Age, pers1.Status);

}//TestPersonProps

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

Рис. 16.1. Методы-свойства и стратегии доступа к полям

Индексаторы

Свойства являются частным случаем метода класса с особым синтаксисом. Еще одним частным случаем является индексатор. Метод-индексатор является обобщением метода-свойства. Он обеспечивает доступ к закрытому полю, представляющему массив. Объекты класса индексируются по этому полю.

Синтаксически объявление индексатора — такое же, как и в случае свойств, но методы get и set

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

Добавим в класс Person свойство children, задающее детей персоны, сделаем это свойство закрытым, а доступ к нему обеспечит индексатор:

const int Child_Max = 20; //максимальное число детей

Person[] children = new Person[Child_Max];

int count_children=0; //число детей

public Person this[int i] //индексатор

{

get {if (i>= 0 && i< count_children)return(children[i]);

else return(children[0]); }

set

{

if (i==count_children && i< Child_Max)

{children[i] = value; count children++;

}

}

Имя у индексатора — this, в квадратных скобках в заголовке перечисляются индексы. В методах get и set, обеспечивающих доступ к массиву children, по которому ведется индексирование, анализируется корректность задания индекса. Закрытое поле count children, хранящее текущее число детей, доступно только для чтения благодаря добавлению соответствующего метода-свойства. Надеюсь, текст процедуры-свойства Count_children сумеете написать самостоятельно. Запись в это поле происходит в методе set индексатора, когда к массиву children добавляется новый элемент.

Протестируем процесс добавления детей персоны и работу индексатора:

public void TestPersonChildren

}

Person pers1 = new Person , pers2 = new Person;

pers1.Fam = "Петров"; persl.Age = 42;

pers1.Salary = 10000;

pers1[pers1.Count_children] = pers2;

pers2.Fam ="Петров"; pers2.Age = 21; pers2.Salary = 1000

Person pers3= new Person("Петрова");

pers1[persl.Count_children] = pers3;

pers3.Fam ="Петрова"; pers3.Age = 5;

Console.WriteLine ("Фам={0}, возраст={1}, статус={2},

pers1.Fam, pers1.Age, pers1.Status);

Console.WriteLine ("Сын={0}, возраст={1}, статус={2},

pers1[0].Fam, persl[0].Age, persl[0]. Status);

Console.WriteLine ("Дочь={0}, возраст={1}, статус= {2};

pers1[1].Fam, pers1[1].Age, pers1[1].Status);

}

Заметьте, индексатор создает из объекта как бы массив объектов, индексированный по соответствующему полю, в данном случае по полю children. На рис. 16.2 показаны результаты вывода.

Рис. 16.2. Работа с индексатором класса

Операции

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

Поделиться с друзьями: