Использование индексаторов (Руководство по программированию в C#)

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

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

public int this[int index]    // Indexer declaration
{
    // get and set accessors
}

Заметки

Тип индексатора и типы его параметров должны иметь по крайней мере такой же уровень доступности, как и сам индексатор. Дополнительные сведения об уровнях доступности см. в разделе Модификаторы доступа.

Дополнительные сведения об использовании индексаторов с интерфейсом см. в разделе Индексаторы интерфейсов.

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

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

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

[System.Runtime.CompilerServices.IndexerName("TheItem")]
public int this [int index]   // Indexer declaration
{
}

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

Пример 1

Описание

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

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

Код

class TempRecord
{
    // Array of temperature values
    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 };

    // To enable client code to validate input 
    // when accessing your indexer.
    public int Length
    {
        get { return temps.Length; }
    }
    // Indexer declaration.
    // If index is out of range, the temps array will throw the exception.
    public float this[int index]
    {
        get
        {
            return temps[index];
        }

        set
        {
            temps[index] = value;
        }
    }
}

class MainClass
{
    static void Main()
    {
        TempRecord tempRecord = new TempRecord();
        // Use the indexer's set accessor
        tempRecord[3] = 58.3F;
        tempRecord[5] = 60.1F;

        // Use the indexer's get accessor
        for (int i = 0; i < 10; i++)
        {
            System.Console.WriteLine("Element #{0} = {1}", i, tempRecord[i]);
        }

        // Keep the console window open in debug mode.
        System.Console.WriteLine("Press any key to exit.");
        System.Console.ReadKey();

    }
}
/* Output:
        Element #0 = 56.2
        Element #1 = 56.7
        Element #2 = 56.5
        Element #3 = 58.3
        Element #4 = 58.8
        Element #5 = 60.1
        Element #6 = 65.9
        Element #7 = 62.1
        Element #8 = 59.2
        Element #9 = 57.5
    */

Индексирование с использованием других значений

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

Пример 2

Описание

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

Код

// Using a string as an indexer value
class DayCollection
{
    string[] days = { "Sun", "Mon", "Tues", "Wed", "Thurs", "Fri", "Sat" };

    // This method finds the day or returns -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 must be in the form \"Sun\", \"Mon\", etc");
    }

    // The get accessor returns an integer for a given string
    public int this[string day]
    {
        get
        {
            return (GetDay(day));
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        DayCollection week = new DayCollection();
        System.Console.WriteLine(week["Fri"]);

        // Raises ArgumentOutOfRangeException
        System.Console.WriteLine(week["Made-up Day"]);

        // Keep the console window open in debug mode.
        System.Console.WriteLine("Press any key to exit.");
        System.Console.ReadKey();
    }
}
// Output: 5

Надежное программирование

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

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

  • Установите максимальное обоснованное ограничение доступности для методов доступа get и set. Это особенно важно для метода доступа set. Дополнительные сведения см. в разделе Ограничение доступности методов доступа (Руководство по программированию на C#).

См. также

Ссылки

Индексаторы (Руководство по программированию в C#)

Свойства (Руководство по программированию в C#)

Основные понятия

Руководство по программированию на C#