Ответ 1
Во-первых, обратите внимание, что одна большая разница в вашем примере (между foreach
и GetEnumerator
) заключается в том, что foreach
гарантирует вызов Dispose()
на итераторе, если итератор IDisposable
. Это важно для многих итераторов (например, которые могут потреблять внешний канал данных).
Собственно, бывают случаи, когда foreach
не так полезен, как хотелось бы.
Во-первых, здесь обсуждался случай "первого элемента" (foreach/определение первой итерации).
Но больше; если вы попытаетесь написать отсутствующий метод Zip
для сшивания двух перечислимых последовательностей вместе (или метод SequenceEqual
), вы обнаружите, что можете использовать foreach
в обеих последовательностях, поскольку это будет выполнять перекрестное соединение. Вы должны использовать итератор непосредственно для одного из них:
static IEnumerable<T> Zip<T>(this IEnumerable<T> left,
IEnumerable<T> right)
{
using (var iter = right.GetEnumerator())
{
// consume everything in the first sequence
foreach (var item in left)
{
yield return item;
// and add an item from the second sequnce each time (if we can)
if (iter.MoveNext())
{
yield return iter.Current;
}
}
// any remaining items in the second sequence
while (iter.MoveNext())
{
yield return iter.Current;
}
}
}
static bool SequenceEqual<T>(this IEnumerable<T> left,
IEnumerable<T> right)
{
var comparer = EqualityComparer<T>.Default;
using (var iter = right.GetEnumerator())
{
foreach (var item in left)
{
if (!iter.MoveNext()) return false; // first is longer
if (!comparer.Equals(item, iter.Current))
return false; // item different
}
if (iter.MoveNext()) return false; // second is longer
}
return true; // same length, all equal
}