На самом деле, строго говоря, все, что вам нужно для использования foreach
, - это открытый GetEnumerator()
метод, который возвращает что-то с методом bool MoveNext()
и свойством ? Current {get;}
. Тем не менее, наиболее распространенным значением этого слова является "то, что реализует IEnumerable
/ IEnumerable<T>
, возвращая IEnumerator
/ IEnumerator<T>
.
Подразумевается, что это включает в себя все, что реализует ICollection
/ ICollection<T>
, например что-либо вроде Collection<T>
, List<T>
, массивы (T[]
) и т. Д. Таким образом, любой стандартный «сбор данных» обычно поддержка foreach
.
Для доказательства первого пункта отлично работает следующее:
using System;
class Foo {
public int Current { get; private set; }
private int step;
public bool MoveNext() {
if (step >= 5) return false;
Current = step++;
return true;
}
}
class Bar {
public Foo GetEnumerator() { return new Foo(); }
}
static class Program {
static void Main() {
Bar bar = new Bar();
foreach (int item in bar) {
Console.WriteLine(item);
}
}
}
Как это работает?
Цикл foreach, такой как foreach(int i in obj) {...}
, что-то вроде:
var tmp = obj.GetEnumerator();
int i; // up to C# 4.0
while(tmp.MoveNext()) {
int i; // C# 5.0
i = tmp.Current;
{...} // your code
}
Однако, есть вариации. Например, если перечислитель (tmp) поддерживает IDisposable
, он также используется (аналогично using
).
Обратите внимание на разницу в размещении объявления "int i
" внутри (C # 5.0) против снаружи (до C # 4.0) цикла , Это важно, если вы используете i
в анонимном методе / лямбде внутри вашего кодового блока. Но это другая история; -p