Язык программирования C#9 и платформа .NET5
Часть 175 из 642 Информация о книге
using System.Collections;namespace CustomEnumerator{// Garage содержит набор объектов Car.public class Garage{private Car[] carArray = new Car[4];// При запуске заполнить несколькими объектами Car.public Garage(){carArray[0] = new Car("Rusty", 30);carArray[1] = new Car("Clunker", 55);carArray[2] = new Car("Zippy", 30);carArray[3] = new Car("Fred", 30);}}}В идеальном случае было бы удобно проходить по внутренним элементам объекта
с применением конструкцииGarageкак в ситуации с массивом значений данных:foreachusing System;using CustomEnumerator;// Код выглядит корректным...Console.WriteLine("***** Fun with IEnumerable / IEnumerator *****\n");Garage carLot = new Garage();// Проход по всем объектам Car в коллекции?foreach (Car c in carLot){Console.WriteLine("{0} is going {1} MPH",c.PetName, c.CurrentSpeed);}Console.ReadLine();К сожалению, компилятор информирует о том, что в классе Garage не реализован метод по имени
, который формально определен в интерфейсеGetEnumerator(), находящемся в пространстве именIEnumerable.System.CollectionsНа заметку! В главе 10 вы узнаете о роли обобщений и о пространстве имен
. Как будет показано, это пространство имен содержит обобщенные версии интерфейсовSystem.Collections.Generic, которые предлагают более безопасный к типам способ итерации по элементам.IEnumerable/IEnumeratorКлассы или структуры, которые поддерживают такое поведение, позиционируются как способные предоставлять вызывающему коду доступ к элементам, содержащимся внутри них (в рассматриваемом примере самому ключевому слову
). Вот определение этого стандартного интерфейса:foreach// Данный интерфейс информирует вызывающий код о том,// что элементы объекта могут перечислятьсяpublic interface IEnumerable{IEnumerator GetEnumerator();}Как видите, метод
возвращает ссылку на еще один интерфейс по имениGetEnumerator(), обеспечивающий инфраструктуру, которая позволяет вызывающему коду обходить внутренние объекты, содержащиеся в совместимом сSystem.Collections.IEnumeratorконтейнере:IEnumerable// Этот интерфейс позволяет вызывающему коду получать элементы контейнера.public interface IEnumerator{bool MoveNext (); // Переместить вперед внутреннюю позицию курсора.object Current { get;} // Получить текущий элемент// (свойство только для чтения).void Reset (); // Сбросить курсор в позицию перед первым элементом.}Если вы хотите обновить тип
для поддержки этих интерфейсов, то можете пойти длинным путем и реализовать каждый метод вручную. Хотя вы определенно вольны предоставить специализированные версии методовGarage,GetEnumerator(),MoveNext()иCurrent, существует более легкий путь. Поскольку типReset()(а также многие другие классы коллекций) уже реализует интерфейсыSystem.ArrayиIEnumerable, вы можете просто делегировать запрос кIEnumeratorследующим образом (обратите внимание, что в файл кода понадобится импортировать пространство именSystem.Array):System.Collectionsusing System.Collections;...public class Garage : IEnumerable{// System.Array уже реализует IEnumerator!private Car[] carArray = new Car[4];public Garage(){carArray[0] = new Car("FeeFee", 200);carArray[1] = new Car("Clunker", 90);carArray[2] = new Car("Zippy", 30);carArray[3] = new Car("Fred", 30);}// Возвратить IEnumerator объекта массива.public IEnumerator GetEnumerator()=> carArray.GetEnumerator();}После такого изменения тип
можно безопасно использовать внутри конструкцииGarage. Более того, учитывая, что методforeachбыл определен как открытый, пользователь объекта может также взаимодействовать с типомGetEnumerator():IEnumerator