Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
CSBasicCourse2ndedPodbelsky / CSBasicCourse2ndedPodbelsky.rtf
Скачиваний:
27
Добавлен:
22.03.2016
Размер:
11.9 Mб
Скачать

12.5. Индексаторы, имитирующие наличие контейнера

Для программиста-пользователя обращение с помощью индексатора к объекту

чужого класса выглядит как обращение к элементу массива. Но массива как

такового в объекте может и не быть. Дело в том, что индексатор можно определить

в классе, где контейнер (например, массив) отсутствует. В этом случае индексатор

просто-напросто заменяет метод. Отличие состоит в синтаксисе обращения.

В качестве примера рассмотрим класс, представляющий две температурные

шкалы. Температура To по абсолютной шкале, введенной Вильямом Томсоном

(лордом Кельвиным), связана с температурой to по шкале Цельсия соотношением:

To = to + 273.16o.

Определим класс Temperature с индексатором, позволяющий получать

значение To по величине to, которую будет задавать параметр индексатора. Так как

температура по Кельвину не может быть отрицательной, то примем, что при to <

-273.16o индексатор будет возвращать значение -1. (Программа 12_06.cs.):

class Temperature

{ // Температура по Кельвину и по Цельсию.

public double this[double t] {

get { return (t < -273.16) ? -1 : t + 273.16; }

}

}

В классе нет массива, и поэтому нет смысла в индексаторе определять

аксессор set. В отличие от предыдущих примеров параметр индексатора и

возвращаемое им значение имеют тип double. Конструктор задается неявно.

Применение индексатора иллюстрирует следующий фрагмент кода:

static void Main()

{

Temperature TK = new Temperature();

double t=43;

Console.WriteLine("TK[{0}] = {1:f2}", t, TK[t]);

t = -400;

Console.WriteLine("TK[{0}] = {1:f2}", t, TK[t]);

t = -273;

Console.WriteLine("TK[{0}] = {1:f2}", t, TK[t]);

}

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

TK[43] = 316,16

TK[-400] = -1,00

TK[-273] = 0,16

Контрольные вопросы

Объясните принципы инкапсуляции и её применения к классам.

Опишите графическое изображение класса в UML.

В чём отличия свойств от полей?

Приведите формат объявления свойства.

Что такое тип свойства?

Что такое тело аксессора в объявлении свойства?

Каким идентификатором представлено в set-аксессоре новое значение свойства?

Объясните назначение механизма автореализуемых свойств.

Что такое скрытые поля.

Объясните роль служебного слова this в индексаторе.

Может ли в одном классе быть несколько индексаторов?

Какой тип допустим для параметра индексатора?

Глава 13. Включение, вложение и наследование

классов

13.1. Включение объектов классов

В соответствии с основной задачей, решаемой при проектировании

программы, входящие в нее классы могут находиться в разных отношениях.

Наиболее простое – отношение независимости классов, то есть независимости

порождаемых ими объектов. Более сложное – отношение включения, для которого

используют названия "имеет" (has-a) и "включает", иначе "является частью" (is-part-

of).

В теории объектно-ориентированного анализа различают две формы

отношения включения – композицию и агрегацию.

При отношении композиции объекты одного класса или нескольких разных

классов входят как поля в объект другого (включающего) класса. Таким образом

включенные объекты не существуют без включающего их объекта.

При

отношении

агрегации

объект

одного

класса

объединяет

уже

существующие объекты других классов. То есть и включающий объект, и

включаемые в него объекты существуют в некотором смысле самостоятельно. При

уничтожении включающего объекта входившие в него объекты сохраняются.

Рассмотрим на примерах особенности реализации на языке C# отношений

композиции и агрегации.

Определим (программа 13_01.cs) класс "точка на плоскости":

class Point

{

double x, y;

public double X { get { return x; } set { x = value; } }

public double Y { get { return y; } set { y = value; } }

}

В классе закрытые поля double x, y определяют координаты точки. Свойства

X и Y обеспечивают удобный доступ к координатам точки, представленно

й

объектом класса Рoint. В классе Рoint нет явно определенного конструктора, и

компилятор добавляет конструктор умолчания – открытый конструктор без

параметров. Координаты создаваемой точки по умолчанию получают нулевые

значения.

Объекты класса Рoint можно по-разному включать в более сложные классы.

Возьмем в качестве такого включающего класса класс Сircle, объект которого

представляет "окружность на плоскости". Объект класса Рoint (точку) будем

использовать в качестве центра окружности.

Начнем с композиции классов

"композиция классов"

и, отложив

объяснения, дадим такое определение класса:

class Сircle

{ // Закрытые поля:

double rad; // радиус окружности

Рoint centre = new Рoint(); // центр окружности

// свойство для радиуса окружности:

public double Rad { get { return rad; }

set { rad = value; } }

// свойство для значения длины окружности:

public double Len { get { return 2*rad*Math.PI; } }

// свойство для центра окружности:

public Рoint Centre { get { return centre; }

set { centre = value; } }

public void display()

{

Console.WriteLine("Centre: X={0}, Y={1};"+" Radius={2},

Length={3, 6:f2}",

centre.X, centre.Y, this.rad, Len);

}

}

В классе Circle два закрытых поля: double rad – радиус окружности и Point

centre – ссылка на объект класса Point. Для инициализации этой ссылки

используется выражение с операцией new, в котором выполняется явное обращение

к конструктору класса Рoint. Тем самым при создании каждого объекта

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

р

окружности.

Три открытых свойства обеспечивают доступ к характеристикам объекта-

окружности: Rad – радиус окружности, Len – длина окружности, Centre – центр

окружности.

В классе определен открытый метод display(), выводящий координаты центра

и значения других характеристик объекта, представляющего окружность.

Так как в классе Circle нет явно определенных конструкторов, то неявно

создается конструктор без параметров, и поля определяемого с его помощью

объекта получают значения по умолчанию.

В следующей программе создан объект класса Circle. Затем с помощью

свойств классов Circle и Point изменены значения его полей. Метод display()

выводит сведения о характеристиках объекта.

static void Main()

{

Circle rim = new Circle();

rim.Centre.X = 10;

rim.Centre.Y = 20;

rim.Rad = 3.0;

rim.display();

}

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

Centre: X=10, Y=20; Radius=3, Length= 18,85

Основное, на что следует обратить внимание, – в программе нет отдельно

существующего объекта класса Point. Именно это является основным признаком

композиции классов. Объект класса Point явно создаётся только при создании

объекта класса Circle.

На рис. 13.1 приведена диаграмма классов, находящихся в отношении

композиции. Конструкторы умолчания, которые добавлены в объявления классов

автоматически, на схемах классов не показаны. Тот факт, что ссылка на объект

класса Point, является значением поля centre объекта класса Circle, никак явно не

обозначен.

Рис. 13.1. Диаграмма композиции классов

Не изменяя класс Рoint, можно следующим образом построить класс

"окружность на плоскости", используя агрегацию классов

"класс: агрегация

классов" (программа 13_2.cs):

class Сircle

{ // Закрытые поля:

double rad; // радиус окружности

Рoint centre; // ссылка без инициализации

public Сircle(Рoint p, double rd) // конструктор

{

centre = p;

rad = rd;

}

public double Rad { ... }

public double Len { ... }

public Рoint Centre { ... }

public void display() { ... }

}

В тексте нового класса Сircle показаны полностью только объявления полей и

конструктор, первый параметр которого – ссылка на объект класса Рoint. Свойства

и метод display() остались без изменений.

При таком измененном определении класса Сircle для создания его объекта

необходимо, чтобы уже существовал объект класса Рoint, с помощью которого в

объекте будет определено значение поля centre.

Диаграмма

"диаграмма агрегации"

классов, находящихся в отношении

агрегации, (см. рис. 13.2), практически та же, что и диаграмма композиции

"диаграмма композиции" . Только в классе Сircle явно присутствует конструктор

общего вида.

Рис. 13.2. Диаграмма агрегации классо

в

В следующей программе (в методе Main) создан объект класса Рoint. Затем с

помощью свойств X и Y изменены значения его полей. На основе этого объекта

конструктор класса Сircle формирует объект "окружность на плоскости". Метод

display() выводит сведения о характеристиках построенного объекта.

static void Main()

{

Рoint pt = new Рoint();

pt.X = 10;

pt.Y = 20;

Сircle rim = new Сircle(pt,10);

rim.display();

}

}

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

Centre: X=10, Y=20; Radius=10, Length=62,83

В отличие от композиции при агрегации в классе Сircle нет явной

инициализации поля centre. Для обеспечения включения объекта класса Рoint в

объект класса Сircle в классе Сircle явно определен конструктор, одним из

параметров которого служит ссылка на объект класса Рoint. В теле конструктора

значение этой ссылки присваивается полю centre класса Сircle.

Соседние файлы в папке CSBasicCourse2ndedPodbelsky