Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Теоретический_курс.doc
Скачиваний:
36
Добавлен:
10.11.2019
Размер:
7.68 Mб
Скачать

3.6.7. Понятие «Индексатор» Понятие «Индексатор»

1. Понятие «Индексатор»

Индексаторы позволяют индексировать экземпляры класса или структуры так же, как массивы. Индексаторы напоминают свойства, но их методы доступа принимают параметры.

В следующем примере ниже, определяется универсальный класс и в качестве средств присвоения и извлечения значений создаются простые методы доступа get и set. Класс Program создает экземпляр этого класса для хранения строк и выводит всего одну строку:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

namespace LC_Console

{

class SampleCollection<T>

{

// Объявляем массив для хранения данных элементов

private T[] arr = new T[100];

// Объявляем индексатор, который позволит клиенту код

// использующий [] при работе с классом

public T this[int i]

{

get

{

// Этот индексатор очень прост, и просто возвращает или задает соответствующий элемент из внутреннего массива

return arr[i];

}

set

{

arr[i] = value;

}

}

}

class Program

{

static void Main(string[] args)

{

// Объявите экземпляр SampleCollection .

SampleCollection<string> stringCollection = new SampleCollection<string>();

// Используем [] конструкцию на типе

stringCollection[0] = "Hello, world!";

Console.WriteLine(stringCollection[0]);

Console.WriteLine("Для продолжения нажмите любую клавишу . . . ");

Console.ReadKey();

}

}

}

Общие сведения об индексаторах:

  • Индексаторы позволяют индексировать объекты аналогично массивам.

  • Метод доступа get возвращает значение. Метод доступа set присваивает значение.

  • Ключевое слово this используется для определения индексаторов.

  • Ключевое слово value используется для определения значения, присваиваемого методом set индексатора.

  • Индексаторы не должны использовать в качестве индекса целочисленное значение; конкретный механизм поиска определяет разработчик.

  • Индексаторы можно перегружать.

  • Индексаторы могут иметь более одного формального параметра, например, при доступе к двухмерному массиву.

2. Использование индексаторов

Индексаторы являются синтаксическим удобством, позволяющим создавать класс, структуру или интерфейс, доступ к которому клиентские приложения получают, как к массиву. Чаще всего индексаторы реализуются в типах, главная цель которых — инкапсуляция внутренней коллекции или массива. Например, предположим, что имеется класс с именем «TempRecord», представляющий набор температур по шкале Фаренгейта, полученных в 10 различных моментов в течение 24 часов. Класс содержит массив с именем «temps» типа float, представляющий температуры, и DateTime, представляющий дату регистрации температур. Путём внедрения в этот класс индексатора, клиенты получат доступ к температурам в экземпляре TempRecord с помощью float temp = tr[4], а не float temp = tr.temps[4].Использование индексатора не только упрощает синтаксис для клиентских приложений, но и делает класс и его назначение интуитивно понятными для других разработчиков.

Чтобы объявить, индексатор для класса или структуры, используйте ключевое слово this как показано в следующем примере:

public int this[int index] // Объявление индексатора

{

// get и set методы доступа

}

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

Значение индексатора не классифицируется как переменная, поэтому не допускается передача значения индексатора как параметра ref или out.

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

[System.Runtime.CompilerServices.IndexerName("TheItem")]

public int this[int index] // Объявление индексатора

{

// get и set методы доступа

}

Этот индексатор будет иметь имя TheItem. Если атрибут имени не предоставлен, используется имя по умолчанию Item.

В следующем примере показано, как объявить закрытое поле массива temps и индексатор. Индексатор обеспечивает прямой доступ к экземпляру tempRecord[i].В качестве альтернативы применению индексатора можно объявить массив как член типа public осуществлять прямой доступ к его членам tempRecord.temps[i].

Обратим внимание, что при вычислении доступа индексатора, например, в инструкции Console.Write вызывается метод доступа get. Таким образом, если не существует метода доступа get, происходит ошибка времени компиляции:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

namespace LC_Console

{

class TempRecord

{

// Массив температурных параметров

private float[] temps = new float[10] { 56.2F, 56.7F, 56.5F, 56.9F, 58.8F,

61.3F, 65.9F, 62.1F, 59.2F, 57.5F };

// Для клиентского код проверки входных данных при обращении к индексатору

public int Length

{

get { return temps.Length; }

}

// Объявление индексатора

// Если индекс находится вне диапазона, массив temps выдаст исключение

public float this[int index]

{

get

{

return temps[index];

}

set

{

temps[index] = value;

}

}

}

class Program

{

static void Main()

{

TempRecord tempRecord = new TempRecord();

// Используем метод доступа "set" индексатора

tempRecord[3] = 58.3F;

tempRecord[5] = 60.1F;

// Используем метод доступа "get" индексатора

for (int i = 0; i < 10; i++)

{

Console.WriteLine("Элемент #{0} = {1}", i, tempRecord[i]);

}

Console.WriteLine("Для продолжения нажмите любую клавишу . . . ");

Console.ReadKey();

}

}

}

/* Выведет:

* Элемент #0 = 56.2

* Элемент #1 = 56.7

* Элемент #2 = 56.5

* Элемент #3 = 58.3

* Элемент #4 = 58.8

* Элемент #5 = 60.1

* Элемент #6 = 65.9

* Элемент #7 = 62.1

* Элемент #8 = 59.2

* Элемент #9 = 57.5

* Для продолжения нажмите любую клавишу . . .

*/

C# не ограничивает тип индексатора типом «integer».Например, может оказаться полезным использовании в индексаторе строки (string). Такой индексатор можно реализовать, выполнив поиск строки в коллекции и возвратив соответствующее значением. Методы доступа можно перегружать, версии типа «string» и «integer» могут сосуществовать совместно в одном пространстве имён.

В этом примере объявляется класс, в котором хранятся дни недели. Объявляется метод доступа get, который принимает строку (название дня недели) и возвращает соответствующее целое число. Например, воскресенье возвращает 0, понедельник возвращает 1 и т. д. Неверный день недели вернёт -1 и выдаст исключение (прервёт выполнение):

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

namespace LC_Console

{

class DayCollection

{

string[] days = { "Пн.", "Вт.", "Ср.", "Чт.", "Пт.", "Суб.", "Вск." };

// Находим день или вернём -1

private int GetDay(string testDay)

{

for (int j = 0; j < days.Length; j++)

{

if (days[j] == testDay)

{

return j;

}

}

throw new System.ArgumentOutOfRangeException(testDay, "testDay должен быть от \"Пн.\" до \"Вск.\"");

}

// "get" возвращает int для полученной строки

public int this[string day]

{

get

{

return (GetDay(day));

}

}

}

class Program

{

static void Main(string[] args)

{

DayCollection week = new DayCollection();

Console.WriteLine(week["Чт."]);

// Выводим ArgumentOutOfRangeException если указали неверную строку

Console.WriteLine(week["Указан 8-ой день недели"]);

Console.WriteLine("Для продолжения нажмите любую клавишу . . . ");

Console.ReadKey();

}

}

}

/* Выведет (после вывода выдаст исключение):

* 5

* Для продолжения нажмите любую клавишу . . .

*/

Существуют два основных способа повышения надежности и безопасности индексаторов.

  • Обрабатывать ошибки на тот случай, если код клиента передаст недопустимое значение индекса. В первом примере, приведённом ранее в этом разделе, класс TempRecord предоставляет свойство Length, позволяющее коду клиента проверить введенные данные перед тем, как передать их индексатору. Также можно поместить код обработки ошибки в индексатор.

  • Установить максимальное обоснованное ограничение доступности для методов доступа get и set. Это особенно важно для метода доступа set.