void Reset(); // Сдвинуть в начальную позицию.
}
Чтобы обеспечить поддержку указанных интерфейсов типом Garage, можно пойти по длинному пути реализации каждого метода вручную. Конечно, ничто не запрещает указать свои версии GetEnumerator(), MoveNext()
using System.Collections;
public class Garage: IEnumerable
// В System.Array уже есть реализация IEnumerator!
private Car[] carArray;
public Cars() {
carArray = new Car[4];
carArray[0] = new Car("FeeFee", 200, 0);
carArray[l] = new Car("Clunker", 90, 0);
carArray[2] = new Car("Zippy, 30, 0);
carArray[3] = new Car("Fjred", 30, 0);}
public IEnumerator GetEnumerator() {
// Возвращает IEnumerator объекта массива.
return carArray.GetEnumerator();
}
}
Теперь, после модификации типа Garage, вы можете использовать этот тип в конструкции foreach без опасений. К тому же, поскольку метод GetEnumerator() определен, как открытый, пользователь объекта тоже может взаимодействовать с типом IEnumerator.
// Manually work with IEnumerator.
IEnumerator I = carLot.GetEnumerator();
i.MoveNext();
Car myCar = (Car)i.Current;
Console.WriteLine("{0} имеет скорость {1} км/ч", myCar.PetName, myCar.CurrSpeed);
Если вы предпочтете скрыть функциональные возможности IEnumerable на объектном уровне, то следует использовать явную реализацию интерфейса.
public IEnumerator IEnumerable.GetEnumerator() {
// Возвращает IEnumerator объекта массива.
return carArray.GetEnumerator();
}
Исходный код.
Проект CustomEnumerator размещен в подкаталоге, соответствующем главе 7.Методы итератора в C#
В .NET 1.x для того, чтобы пользовательские коллекции (такие, как Garage) допускали применение конструкции foreach в операциях, подобных перечислению, реализация интерфейса IEnumerable (и, как правило, интерфейса IEnumerator) была обязательной. В C# 2005 предлагается альтернативный вариант построения типов, позволяющих применение цикла foreach, – с помощью
В упрощённой интерпретации итератор является членом, указывающим порядок возвращения внутренних элементов контейнера при их обработке с помощью foreach. И хотя метод итератора все равно должен называться GetEnumerator(), а возвращаемое значение все равно должно иметь тип IEnumerator, при таком подходе ваш пользовательский класс уже
public class Garage { // Без реализации IEnumerator!
private Car[] carArray;
…
// Метод итератора.
public IEnumerator GetEnumerator() {
foreach (Car с in carArray) {
yield return c;
}
}
}
Обратите внимание на то, что данная реализация GetEnumerator() осуществляет "проход" по вложенным элементам, используя внутреннюю логику foreach, и возвращает объекты Car вызывающей стороне, используя новую синтаксическую конструкцию yield return. Ключевое слово yield используется для того, чтобы указать значение (или значения), возвращаемые конструкции foreach вызывающей стороны. Когда в программе встречается оператор yield return, сохраняется текущая позиция, и именно с этой позиции выполнение будет продолжено при следующем вызове итератора.
Когда компилятор C# обнаруживает метод итератора, в рамках области видимости соответствующего типа (в данном случае это Garage) динамически генерируется вложенный класс. Этот автоматически сгенерированный класс реализует интерфейсы IEnumerable и IEnumerator и указывает необходимые параметры членов GetEnumerator(), MoveNext(), Reset() и Current. Если теперь загрузить данное приложение в ildasm.exe, то будет видно, что внутренняя реализация GetEnumerator() в объекте Garage использует сгенерированный компилятором тип (который в данном примере получает имя