С#, в чем смысл или преимущество индексатора?

Выполняя чтение кода, я наткнулся на этот фрагмент, который я раньше не видел:

public SomeClass {
  public someInterface this[String strParameter] {
    get {
      return SomeInternalMethod(strParameter);
    }
  }
}

Похоже, он называется следующим образом:

SomeClass _someClass = new SomeClass();
SomeInterface returnedValue = _someClass["someString"];

Меня интересует, где эта функция была бы уместна или какова цель написания в этом стиле. Например, почему это предпочтительнее простого вызова функции?


person Michael Gattuso    schedule 06.01.2010    source источник
comment
Это естественно, изящно, радует глаз, расширяет понимание. Вызовы методов скучны. Проверьте это: steve-yegge.blogspot. ком/2006/03/   -  person Hans Passant    schedule 07.01.2010


Ответы (11)


См. спецификацию языка, раздел 10.9, в которой говорится:

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

Индексаторы и свойства очень похожи по своей концепции, но различаются следующим образом:

  • Свойство идентифицируется по его имени, тогда как индексатор идентифицируется по его сигнатуре.
  • Доступ к свойству осуществляется через простое имя (§7.5.2) или доступ к члену (§7.5.4), тогда как доступ к элементу индексатора осуществляется через доступ к элементу (§7.5.6.2).
  • Свойство может быть статическим членом, тогда как индексатор всегда является членом-экземпляром.
  • Метод доступа get свойства соответствует методу без параметров, тогда как метод доступа get индексатора соответствует методу с тем же списком формальных параметров, что и у индексатора.
  • Метод доступа set свойства соответствует методу с одним параметром с именем value, тогда как метод доступа set индексатора соответствует методу с тем же списком формальных параметров, что и индексатор, плюс дополнительный параметр с именем value.
  • Объявление локальной переменной с тем же именем, что и у параметра индексатора, является ошибкой времени компиляции.
  • В объявлении переопределяющего свойства доступ к унаследованному свойству осуществляется с использованием синтаксиса base.P, где P — имя свойства. В объявлении переопределяющего индексатора доступ к унаследованному индексатору осуществляется с использованием синтаксиса base[E], где E — список выражений, разделенных запятыми.
person Eric Lippert    schedule 06.01.2010
comment
Я знаю, что не должен перехватывать это, но, может быть, вы могли бы написать в блоге, почему не существует статических индексаторов? Я был в шоке, когда увидел, что их не существует только потому, что это такой красивый, сокращенный способ. (До того, как Джон приводит хороший пример кодировки [UTF8] :)) - person Michael Stum; 07.01.2010
comment
Я только что проверил архив примечаний к дизайну, и там нет ничего, оправдывающего решение не использовать статические индексаторы. Поэтому я должен вернуться к моему обычному объяснению того, почему у нас нет функции: никто не думал, что это достаточно хорошая идея, чтобы тратить деньги на ее воплощение в жизнь. Я предполагаю, что это же объяснение также объясняет, почему индексаторы не могут быть универсальными. - person Eric Lippert; 07.01.2010
comment
Круто :) Хорошая вещь в объяснении по умолчанию заключается в том, что оно не полностью закрывает дверь, когда это возможно. Я понятия не имею, насколько тяжела эта функция (и я не буду дисквалифицировать себя, сказав, что это всего лишь небольшое изменение! без знания того, как на самом деле работают компилятор и CLR), но это лучше, чем Мы не сделали этого из-за X, Y и Z, которые делают невозможным его реализацию. - person Michael Stum; 07.01.2010
comment
Отличная информация - я не знал, что это называется индексатором, поэтому я не знал, с чего начать искать его определение. Думаю, я сбит с толку тем, почему автор использует индексатор только для чтения со строковым параметром. Если бы это был int, я бы получил его, так как вы можете использовать его с циклами и т. д. Использование строкового параметра дает структуру типа словаря, но он доступен только для чтения, поэтому он действительно похож на метод, поскольку вы не можете передать ему значение. - person Michael Gattuso; 07.01.2010

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

Насколько я понимаю, вот мотивация для использования индексатора:

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

Лучший пример, который я могу придумать, — это класс DataRow в ADO.NET. Если вы хотите получить значение пятой ячейки DataRow, вы можете использовать DataRow.Item[4] или DataRow[4]. Последняя форма является удобным и логичным сокращением, и она довольно хорошо демонстрирует, почему вы хотите использовать индексатор. Для пользователя DataRow может рассматриваться как просто набор ячеек (хотя на самом деле это нечто большее), поэтому имеет смысл иметь возможность напрямую получать и устанавливать значения ячеек, не вспоминая, что вы на самом деле получение/установка Item.

Надеюсь, это поможет.

person devuxer    schedule 06.01.2010

Во многих случаях синтаксис index имеет большой смысл. Это особенно полезно, если SomeClass представляет какую-то коллекцию.

person Erich    schedule 06.01.2010

Это позволяет вам выполнять поиск в ассоциативном массиве (также известный как «стиль словаря»), как вы упомянули в своем вопросе.

И в этом весь смысл. Некоторым людям это нравится, особенно людям, пришедшим из языков, в которые это встроено, таких как Python или PHP.

person Randolpho    schedule 06.01.2010

Ключевое слово this является индексатором

из http://msdn.microsoft.com/en-us/library/6x16t2tx.aspx

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

person Simon    schedule 06.01.2010
comment
Что вы имеете в виду: ключевое слово this является индексатором? - person spender; 07.01.2010

Возможно, вы уже сталкивались с чем-то подобным:

var myList = new List<string>();
myList.Add("One");
myList.Add("Two");
myList.Add("Three");

for(int i = 0; i < myList.Count; i++) {
    string s = myList[i];
}

Индексатор — это «первичный ключ» объекта, реализующего коллекцию. Это просто сокращенный способ написания функции типа .GetValue(index) - синтаксический сахар, если хотите. Но это также делает намерение ясным.

person Michael Stum    schedule 06.01.2010

Это реализация оператора индекса [ ].

person psychotik    schedule 06.01.2010

Это перегрузка оператора. Полезно, если вы пишете класс коллекции, например, когда имеет смысл получить к нему доступ, используя нотацию массива, например. коллекция [некоторый индекс].

Конечно, вы могли бы написать эквивалент функции collection.GetElement(someIndex), но это стиль/удобочитаемость.

person Paolo    schedule 06.01.2010

Ну, вы можете использовать этот метод в классе пар ключ-значение.

Я не уверен, что аналогичный класс находится в С#, но в С++ STL есть класс карты, в котором вы вызываете метод с SomeObject["key"], и он возвращает «значение», связанное с этим ключом.

person zmbush    schedule 06.01.2010
comment
Аналогичным классом в C# является System.Collections.Generic.Dictionary (за исключением того, что Dictionary использует хэш-поиск, а map упорядочивает ключи) - person Andrew Shepherd; 07.01.2010

Это называется Indexer, они позволяют вам использовать List‹>, ArrayList, Dictionary‹> и все другие коллекции, использующие синтаксис массива.

Это просто синтаксический сахар, но при правильном использовании он дает некоторую удобочитаемость.

person albertein    schedule 06.01.2010

На мой взгляд, это просто удобство синтаксиса. Где бы вы НЕ использовали его:

public SomeClass {
  private int[] nums;
  public GetVal this[int ind] {
    get {
      return nums[ind]; // this is pointless since array is already indexed
    }
  }
}

Где бы вы выиграли от этого:

public Matrix {
  private Dictionary<string, double> matrixData; // Matrix indecies, value
  public double this[int row, int col] {
    get {
      return matrixData[string.Format("{0},{1}", row, col)];
    }
  }
}

Как вы можете видеть, по какой-то причине ваши данные представляют собой словарь, индексированный строковым ключом, и вы хотите вызвать его с двумя целочисленными индексами и все еще не хотите менять свой тип данных:

Matrix m = new Matrix();
...
Console.WriteLine( m[1,2] );
person max    schedule 25.01.2015