Интерфейсы IEnumerable и IEnumerator
Чтобы начать изучение процесса реализации существующих интерфейсов .NET, давайте сначала рассмотрим роль
IEnumerable и IEnumerator. Напомним, что C # поддерживает ключевое слово с именем foreach, которое позволяет вам
перебирать содержимое любого типа массива:
// Iterate over an array of items.
int[] myArrayOfInts = {10, 20, 30, 40};
foreach(int i in myArrayOfInts)
{
Console.WriteLine(i);
}
Хотя может показаться, что эту конструкцию могут использовать только типы массивов, правда в том, что
Любой тип, поддерживающий метод с именем GetEnumerator (), может быть оценен конструкцией foreach.
иллюстрируй, следуй за мной!
Предположим, у нас есть класс Garage:
// Garage contains a set of Car objects.
public class Garage
{
private Car[] carArray = new Car[4];
// Fill with some Car objects upon startup.
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, используя foreach
построить, как массив значений данных:
// This seems reasonable ...
public class Program
{
static void Main(string[] args)
{
Console.WriteLine("***** Fun with IEnumerable / IEnumerator *****\n");
Garage carLot = new Garage();
// Hand over each car in the collection?
foreach (Car c in carLot)
{
Console.WriteLine("{0} is going {1} MPH",
c.PetName, c.CurrentSpeed);
}
Console.ReadLine();
}
}
К сожалению, компилятор сообщает вам, что класс Garage не реализует метод с именем
GetEnumerator (). Этот метод формализован интерфейсом IEnumerable, который находится в пространстве имен System.Collections.
Классы или структуры, которые поддерживают это поведение, объявляют, что они могут раскрыть содержимое
подпункты к вызывающей стороне (в этом примере, само ключевое слово foreach). Вот определение этого стандартного интерфейса .NET:
// This interface informs the caller
// that the object's subitems can be enumerated.
public interface IEnumerable
{
IEnumerator GetEnumerator();
}
Как видите, метод GetEnumerator () возвращает ссылку на еще один интерфейс с именем
System.Collections.IEnumerator. Этот интерфейс обеспечивает инфраструктуру, позволяющую вызывающей стороне проходить внутренние объекты, содержащиеся в IEnumerable-совместимом контейнере:
// This interface allows the caller to
// obtain a container's subitems.
public interface IEnumerator
{
bool MoveNext (); // Advance the internal position of the cursor.
object Current { get;} // Get the current item (read-only property).
void Reset (); // Reset the cursor before the first member.
}
Если вы хотите обновить тип Garage для поддержки этих интерфейсов, вы могли бы пройти долгий путь и
реализовать каждый метод вручную. Хотя вы, безусловно, можете предоставить индивидуальные версии
GetEnumerator (), MoveNext (), Current и Reset (), есть более простой способ. Поскольку тип System.Array (как и многие другие классы коллекций) уже реализует IEnumerable и IEnumerator, вы можете просто делегировать запрос System.Array следующим образом:
using System.Collections;
...
public class Garage : IEnumerable
{
// System.Array already implements 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);
}
public IEnumerator GetEnumerator()
{
// Return the array object's IEnumerator.
return carArray.GetEnumerator();
}
}
После того, как вы обновили свой тип Garage, вы можете безопасно использовать этот тип в конструкции C # foreach. Кроме того, учитывая, что метод GetEnumerator () был определен публично, пользователь объекта может также взаимодействовать с типом IEnumerator:
// Manually work with IEnumerator.
IEnumerator i = carLot.GetEnumerator();
i.MoveNext();
Car myCar = (Car)i.Current;
Console.WriteLine("{0} is going {1} MPH", myCar.PetName, myCar.CurrentSpeed);
Однако, если вы предпочитаете скрыть функциональность IEnumerable от уровня объекта, просто сделайте
использование явной реализации интерфейса:
IEnumerator IEnumerable.GetEnumerator()
{
// Return the array object's IEnumerator.
return carArray.GetEnumerator();
}
При этом пользователь случайного объекта не найдет метод GetEnumerator () в Garage, а
Конструкция foreach при необходимости получит интерфейс в фоновом режиме.
Адаптировано из Pro C # 5.0 и .NET 4.5 Framework