Здесь возникает вопрос: "Как сконфигурировать класс (или структуру), чтобы обеспечить поддержку соответствующих функциональных возможностей?" Индексатор в C# представляет собой несколько "искаженное" свойство. Для создания индексатора в самой простой форме используется синтаксис this[]. Вот как может выглядеть подходящая модификации типа Garage.
// Добавление индексатора в определение класса.
public class Garage: IEnumerable { // для каждого элемента
…
// Использование ArrayList для типов Car.
private ArrayList carArray = new ArrayList();
// Индексатор возвращает тип Car, соответствующий
// Числовому индексу.
public Car this[int pos] {
// ArrayList тоже имеет индексатор!
get { return (Car)carArray[pos]; }
set { carArray.Add(value); }
}
}
Если не обращать внимания на ключевое слово this, то объявление индексатора очень похоже на объявление свойства в C#. Но следует подчеркнуть, что индексаторы не обеспечивают иных функциональных возможностей массива, кроме возможности использования операции индексирования, Другими словами, пользователь объекта не может применить программный код, подобный следующему.
// Используется свойство ArrayList.Count? Нет!
Console.WriteLine("Машин в наличии: {0} ", carLot. Count);
Для поддержки этой функциональной возможности вы должны добавить свое свойство Count в тип Garage и, соответственно, делегат.
public class Garage: IEnumerable {
…
// Локализация/делегирование в действии снова.
public int Count { get { return carArray.Count; } }
}
Итак, индексаторы – это еще одна синтаксическая "конфетка", поскольку соответствующих функциональных возможностей можно достичь и с помощью "обычных" методов. Например, если бы тип Garage не поддерживал индексатор, все равно можно было бы позволить "внешнему миру" взаимодействовать с внутренним массивом, используя для этого именованное свойство или традиционные методы чтения и модификации данных (accessor/mutator). Но при использовании индексаторов пользовательские типы коллекции лучше согласуются со структурой библиотек базовых классов .NET.
Исходный код.Проект SimpleIndexer размещен в подкаталоге, соответствующем главе 9.
Вариации индексатора для типа Garage
В своем текущем виде тип Gаrage определяет индексатор, который позволяет вызывающей стороне идентифицировать внутренние элементы, используя число-вое значение. Но это не является непременным требованием метода индексатора. Предположим, что объекты Car содержатся в System.Collections.Specialized. ListDictionary, а не в ArrayList. Поскольку типы ListDictionary позволяют доступ к содержащимся типам с помощью ключевых маркеров (таких как, например, строки), можно создать новый индексатор Garage, подобный показанному ниже.
public class Garage: IEnumerable {
private ListDictionary carDictionary = new ListDictionarу();
// Этот индексатор возвращает соответствующий тип Car
// на основе строкового индекса.
public Car this[string name] {
get { return (Car)carDictionary[name]; }
set { carDictionary[name] = value; }
}
public int Length { get { return carDictionary.Count; } }
public IEnumerator GetEnumerator() { return carDictionary.GetEnumerator(); }
}
Вызывающая сторона теперь может взаимодействовать с машинами внутри так, как показано ниже,
public class Program {
static void Main(string[] args) {
Console:WriteLine("***** Забавы с индексаторами *****\n");
Garage carLot = new Garage();
// Добавление именованных машин в гараж.
carLot["FeeFee"] = new Car("FeeFee", 200, 0);
carLot["Clunker"] = new Car("Clunker", 90, 0);
carLot["Zippy"] = new Car("Zippy", 30, 0);
// Доступ к Zippy.
Car zippy = carLot["Zippy"];
Console.WriteLine("{0} едет со скоростью {1} км/ч", zippy.PetName, zippy.CurrSpeed);
Console.ReadLine();
}
}
Индексаторы могут быть и перегруженными. Так, чтобы позволить вызывающей стороне доступ к внутренним элементам посредством числового индекса или строковых значений, вы можете определить множество индексаторов для одного типа.
Исходный код.Проект StringIndexer размещен в подкаталоге, соответствующем главе 9.
Внутреннее представление индексаторов типов
Мы рассмотрели примеры метода индексатора в C#, и пришло время выяснить, как представляются индексаторы в терминах CIL. Если открыть числовой индексатор типа Garage, то будет видно, что компилятор C# создает свойство Item, которое сводится к подходящей паре методов get/set.
Читать дальше