Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Podbelsky_V_V_C_Bazovy_kurs.pdf
Скачиваний:
69
Добавлен:
02.06.2015
Размер:
1.73 Mб
Скачать

226

Г л а в а 1 2

 

 

строка результатов. В последней строке – значение свойства RealValue.

Аксессор get выполняется, когда из кода, внешнего по отношению к классу или его объекту, выполняется «чтение» значения свойства. При этом в точку вызова возвращается некоторое значение или ссылка на объект. Тип значения или ссылки соответствует типу в объявлении свойства. При этом возможны неявные приведения типов. Например, если getаксессор возвращает значение типа int, а тип свойства double, то будет автоматически выполнено приведение типов. Get-аксессор подобен методу без параметров, возвращающему значение или ссылку с типом свойства.

Если внешний по отношению к классу или его объекту код присваивает некоторое значение свойству, то вызывается set-аксессор этого свойства. В теле этого аксессора присвоенное свойству значение представлено специальной переменной с фиксированным именем value. Тип этой переменной совпадает с типом свойства. У set-аксессора возвращаемое значение отсутствует. Можно считать, что set-аксессор функционально подобен методу с одним параметром. У этого параметра тот же тип, что и тип свойства, и фиксированное имя value.

Можно использовать в объявлении свойства только один из аксессоров. Это позволяет вводить свойства только для записи (изменения) и только для чтения. Возникает вопрос: «Чем открытое свойство, обеспечивающее только чтение, отличается от открытого поля, объявленного с модификатором readonly?» Основное отличие в том, что поле хранит некоторое значение, которое не может изменить процесс чтения из этого поля. При чтении значения свойства есть возможность выполнить заранее запланированные действия (вычисления), причём никаких ограничений на характер этих действий (вычислений) не накладывается. Результат вычислений свойства может зависеть, например, от состояния среды, в которой выполняется программа, или от влияния процессов, выполняемых параллельно. Пример поля с модификатором readonly: “дата рождения”. Свойство “точный возраст” должно вычисляться с учетом конкретного момента обращения к этому свойству.

Средства взаимодействия с объектами

227

 

 

12.3.Автореализуемые свойства

ВC# 3.0 включено новое средство, позволяющее частично автоматизировать кодирование классов одного достаточного ограниченного вида. Эту возможность обеспечивают свойства, специфицированные как автоматически реализуемые (Automatically implemented properties). Покажем это на примере.

Пример кода, который готовит программист [1]:

public class Point { public int X {get; set;} public int Y {get; set;}

}

В классе Point нет явно определенных полей, а два открытых свойства представляют собой “полуфабрикаты” автореализуемых свойств. В автореализуемых свойствах отсутствуют тела аксессоров, но каждый из аксессоров может иметь модификаторы доступа. На основе такой декларации компилятор автоматически формирует эквивалентное ей объявление класса:

public class Point{ private int x; private int y;

public int X {get {return x;} set{x=value;}} public int Y {get {return y;} set{y=value;}}

}

В автоматически построенном объявлении появились два закрытых поля, типы которых совпадают с типами свойств. Поле, отнесенное к автореализуемому свойству, называют hidden backing field – скрытым полем заднего плана. Именно с этими полями ассоциированы свойства Х и У, которые были “запланированы” программистом. Имена скрытых полей компилятор формирует по правилам, которые известны только ему. В нашем примере х и у – это только условные обозначения, выбранные нами для иллюстрации. Никакие прямые обращения к этим скрытым полям невозможны ни в классе, ни за его пределами. Доступ к этим полям обеспечивают только ассоциированные с ними автореализуемые свойства.

228

Г л а в а 1 2

 

 

В приведенном примере скрытые поля класса Point доступны с помощью свойств Х и У как для чтения, так и для изменений. При объявлении автореализуемого свойства можно ограничить доступ к связанному с ним полю, например, разрешив извне класса только чтение. Сразу возникает вопрос: «Как задать значение такого поля, если имя его недоступно и неизвестно (известно только компилятору), а автореализуемое свойство разрешает только чтение?» Наиболее прямое решение – явно объявить в классе с автореализуемыми полями конструктор и в нём через закрытое свойство устанавливать значение скрытого поля. Следующая программа иллюстрирует предлагаемые решение:

// 12_03.cs автореализуемые свойства using System;

public class Point

{

public int X { get; private set; } public int Y { get; private set; }

public Point(int xi, int yi) { X = xi; Y = yi; }

}

class Program

{

static void Main()

{

Point a = new Point(12, 7); Console.WriteLine(a.Y);

}

}

Результат выполнения программы:

7

12.4. Индексаторы

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

ссылка_на_объект [индексное_выражение]

Средства взаимодействия с объектами

229

 

 

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

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

Объявление индексатора имеет следующий формат:

модификаторы_индексатораopt

тип this [спецификация параметров] { декларация get-аксессораopt декларация set-аксессораopt

}

Модификаторы_индексатора те же, что и для свойств за одним исключением – для индексаторов нельзя использовать модификатор static. Индексаторы не могут быть статическими

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

– заключённые в фигурные скобки объявления аксессоров get

иset.

Если у индексатора только один параметр, формат определения индексатора можно изобразить так:

модификаторыopt тип_результата this [тип_индекса индекс] {

get {

операторы get_аксессора

}

set {

230

Г л а в а 1 2

 

 

опрераторы set_аксессора

}

}

Операторы get_аксессора предназначены для получения значения элемента, соответствующего значению индекса. Операторы set_аксессора выполняют присваивания значения элементу, соответствующему значению индекса Вызов индексатора, т. е. выражение

имя_объекта [список аргументов]

может размещаться слева и справа от операции присваивания. В первом случае выполняется аксессор set, во втором – аксессор get. В теле аксессора set значение, которое нужно использовать для изменения элемента коллекции, всегда представлено служебным словом value.

Вкачестве примера класса с индексатором определим класс Work_hours для представления отработанных часов по дням недели. В массиве должно быть 7 элементов, с индексами от 0 (для понедельника) до 6 (для воскресенья). Значения элементов массива – количества отработанных часов по дням недели от 0 (не рабочий день) до 14 часов (больше работать запрещено). Для обращения к элементам массива в класс введем индексатор.

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

//12_04.cs индексатор – рабочие часы дней недели. using System;

class Work_hours

{

int[] days; // Часы по дням недели public Work_hours() // Конструктор

{

Средства взаимодействия с объектами

231

 

 

days = new int[7];

}

public int this[int d] // Индексатор

{

get { return (d < 0 || d > 6) ? -1 : days[d]; } set

{

if (d < 0 || d > 6 || value < 0 || value > 14) Console.WriteLine("Недопустимо: день={0}, часы={1}!",d,value);

else days[d] = value;

}

}

}

class Program

{

static void Main()

{

Work_hours week = new Work_hours(); week[0] = 7; // Понедельник

week[2] = 17; //Недопустимые данные week[3] = 7; // Четверг

week[6] = 7; // Воскресенье Console.WriteLine("Рабочая неделя: "); for (int i = 0; i < 7; i++)

Console.Write("day[{0}] = {1} ", i, week[i]); Console.WriteLine();

Console.WriteLine("day[{0}] = {1}\t", 8, week[8]);

}

}

Результат выполнения программы:

Недопустимо: день=2, часы=17!

 

Рабочая неделя:

 

 

day[0] = 7

day[1] = 0

day[2] = 0

day[3] = 7

day[4] = 0

day[5] = 0

day[6] = 7

 

day[8] = -1

 

 

 

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

232

Г л а в а 1 2

 

 

Dictionary включим в качестве полей два массива строк – массив исходных слов, например, на английском языке, и массив переводных эквивалентов. Массив исходных слов будем заполнять в конструкторе при создании словаря-объекта. Элементам массива переводных эквивалентов будем присваивать значения с использованием индексатора. Параметр индексатора будет иметь тип string. Задавая в квадратных скобках слово в виде строки, получим доступ к элементу с его переводным эквивалентом. Определение класса может быть таким (программа 12_05.cs):

class Dictionary { // Словарь string [] words; // Слова string [] trans; // Переводы

public Dictionary(params string[] str) // Конструктор

{

words = new string[str.Length]; trans = new string[str.Length]; int ind=0;

foreach (string s in str)

words[ind++] = s; // Заполнили массив слов

}

int search(string str) { // Поиск слова

for (int i = 0; i < words.Length; i++) if (words[i] == str) return i;

return -1;

}

public string this[string w] { set { int ind = search(w);

if (ind == -1) Console.WriteLine("Слова Нет!");

else trans[ind] = value;

}

get { int ind = search(w);

if (ind == -1) return "Слова Нет!"; else return trans[ind]; }

}

}

В классе Dictionary две ссылки words и trans на массив слов и на массив их переводных эквивалентов. Собственно массивы как объекты создаются в конструкторе. У конструк-

Средства взаимодействия с объектами

233

 

 

тора параметр с модификатором params, позволяющий использовать переменное число аргументов. Реальное количество аргументов определяется как str.Length. Это значение определяет размеры массивов, адресуемых ссылками words и trans. Строкиаргументы конструктора присваиваются элементам массива words[ ] в цикле foreach. Массив переводов trans[ ] остается незаполненным.

В классе определен вспомогательный закрытый метод search(), для поиска в словаре (в массиве words[ ] исходных слов) слова, заданного с помощью аргумента. Метод вернет индекс слова либо -1, если слово отсутствует. Метод search() используется в индексаторе. В аксессоре set определяется индекс ind того элемента массива trans[ ], которому нужно присвоить новое значение переводного эквивалента. Если поиск неудачен – выводится сообщение, иначе элементу trans[ind], присваивается значение переводного эквивалента. Аксессор get возвращает значение trans[ind] либо строку с сообщением, что слова нет в словаре.

Для иллюстрации возможностей класса dictionary и его индексатора приведем следующий фрагмент кода:

static void Main()

{

Dictionary number = new Dictionary("zero", "one", "two");

number["«zero"] = "нуль"; number["one"] = "один"; number["two"] = "2";

Console.WriteLine("number[\"one\"]: " + number["one"]);

Console.WriteLine("number[\"three\"]: " + number["three"]);

Console.WriteLine("number[\"two\"]: " + number["two"]);

}

В методе Main() создан один объект класса Dictionary. В объекте-словаре всего три слова, у которых вначале нет переводных эквивалентов. Для задания переводов используются выражения с индексами. При обращении к отсутствующему слову значением выражения number[«three»] будет строка «Слова Нет!».

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