Индексаторы

Вы знаете, что доступ к индивидуальным элементам, содержащимся в стандартных массивах, осуществляется через операцию индекса ([ ]).  В языке C# имеется возможность проектировать специальные классы и структуры, которые могут быть индексированы подобно стандартному массиву, посредством определения индексатора (альтернатива массиву). Если вы можете обойтись массивами, забудьте об индексаторах. Важно, что основное назначение индексатора — предоставить пользователю функциональные возможности, аналогичные работе с массивом. Это языковое средство полезно при создании специальных типов коллекций, когда их размер заранее неизвестен.

Если все же вам еще интересно, зачем они нужны, то начнем с определений. Индексаторы могут быть одно- или многомерными.

Одномерные индексаторы

Общая форма одномерного индексатора:

тип_элемента this[int индекс]
{
   get 
   {
     // Возврат значения, которое определяет индекс.
   }
   set 
   {
    // Установка значения, которое определяет индекс.
   }
}

где тип_элемента обозначает конкретный тип элемента индексатора. Следовательно, у каждого элемента, доступного с помощью индексатора, должен быть определенный тип_элемента. Этот тип соответствует типу элемента массива. Параметр индекс получает конкретный индекс элемента, к которому осуществляется доступ. Формально этот параметр совсем не обязательно должен иметь тип int, но чаще всего используется целочисленный тип данного параметра.

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

Давайте рассмотрим пример:

using System;
namespace индексаторы
{
   class MA
   {
      object[] ar;
      public int Leng;
      public MA(int Size)
      {
         ar = new object[Size];
         Leng = Size;
      }
     // Создаем простейший индексатор
     public object this[int index]
     {
        set
        {
           ar[index] = value;
        }
        get
        {
           return ar[index];
        }
     }
  }
  class Program
  {
     static void Main()
     {
        MA a1 = new MA(Size: 5);
        // Инициализируем каждый экземпляра класса a1
        a1[0] = 5;
        a1[1] = 3.14;
        a1[2] = 'Y';
        a1[3] = "Russia";
        a1[4] = true;
        for (int i = 0; i < 5; i++)
           Console.Write("{0}\t", a1[i]);
        Console.WriteLine("\nДлина - {0}", a1.Leng);
        Console.ReadLine();
     }
   }
}

Результат:

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

На применение индексаторов накладываются два существенных ограничения. Во-первых, значение, выдаваемое индексатором, нельзя передавать методу в качестве параметра ref или out, поскольку в индексаторе не определено место в памяти для его хранения. И во-вторых, индексатор должен быть членом своего класса и поэтому не может быть объявлен как static.

Многомерные индексаторы

Пример индексатора, принимающего несколько параметров:

 

using System;
namespace индексаторы
{
   class MA2
   {
      int[,] ar;
      // Размерность двухмерного массива
      public int r, c;
      public int Leng;
      // конструктор
      public MA2(int rows, int cols)
      {
         this.r = rows;
         this.c = cols;
         ar = new int[this.r, this.c];
         Leng = r * c;
      }
      // Индексатор
      public int this[int ind1, int ind2]
      {
         get
         {
            return ar[ind1, ind2];
         }
         set
         {
            ar[ind1, ind2] = value;
         }
      }
   }
   class Program
   {
      static void Main(string[] args)
      {
         Random ran = new Random();
         MA2 a2 = new MA2(4, 5);
         for (int i = 0; i < a2.r; i++)
         {
            for (int j = 0; j < a2.c; j++)
            {
               a2[i, j] = ran.Next(50,90);
               Console.Write(a2[i, j] + "\t");
            }
            Console.WriteLine();
         }
         Console.ReadLine();
      }
   }
}

Результат:

После инициализации индексатора далее с ним можем работать как с двухмерным массивом целых чисел. Небольшой плюс индексатора: размерности (число строк и столбцов) являются членами класса. Замените 2 строку в методе Main( ) на:

    int[,] a2 = new int[4, 5];

т.е. объявим массив a2. Тогда в задании верхних границ индексов будет ошибки типа:

«System.Array» не содержит определения для «r» и не был найден метод расширения «r», принимающий тип «System.Array» в качестве первого аргумента.

Впрочем, если объявите класс МА3:

class MA3
{
   public int[,] ar;
   // Размерность двухмерного массива
   public int r, c;
   public int Leng;
   // конструктор
   public MA3(int rows, int cols)
   {
      this.r = rows;
      this.c = cols;
      ar = new int[this.r, this.c];
      Leng = r * c;
   }
}

и внесете изменения в вызов элементов массива (выделено красным):

class Program
{
   static void Main(string[] args)
   {
      Random ran = new Random();
      MA3 a2 = new MA3(4, 5);
      for (int i = 0; i < a2.r; i++)
      {
         for (int j = 0; j < a2.c; j++)
         {
            a2.ar[i, j] = ran.Next(50,90);
            Console.Write(a2.ar[i, j] + "\t");
         }
         Console.WriteLine();
      }
      Console.ReadLine();
   }
}

то вы получите практически тоже, что и с индексатором. Плюс только в том, что вы обращаетесь к элементу индексатора a2[i, j], а к элементу массива через поле ar: a2.ar[i, j].

Примечания.
  1. Современные языки программирования предлагают вам различные варианты реализации тех или иных классов, выбор наиболее удобного – это ваш выбор.
  2. Объявление индексатора очень похоже на объявление свойств с аксессорами set и get. Поэтому понятно, почему свойства и индексаторы относятся к член-функциям класса.

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Пролистать наверх