Обновление 17.07.2012: по-видимому, начиная с C # 5.0, поведение foreach
, описанное ниже, было изменено, и " использование итерационной переменной foreach
во вложенном Лямбда-выражение больше не дает неожиданных результатов."Этот ответ не относится к C # ≥ 5.0.
@ Джон Скит и все, кто предпочитает ключевое слово foreach.
Проблема с «foreach» в C # до версии 5.0 заключается в том, что она несовместима с тем, как работает эквивалент «для понимания» на других языках, и с тем, как ожидайте, что это сработает (личное мнение изложено здесь только потому, что другие высказали свое мнение относительно читабельности). См. Все вопросы, касающиеся " Доступ к измененному закрытию "
а также " Закрытие переменной цикла, считающейся вредной ". Это только «вредно» из-за способа, которым «foreach» реализован в C #.
Возьмите следующие примеры, используя функционально эквивалентный метод расширения, который приведен в ответе @Fredrik Kalseth.
public static class Enumerables
{
public static void ForEach<T>(this IEnumerable<T> @this, Action<T> action)
{
foreach (T item in @this)
{
action(item);
}
}
}
Извинения за чрезмерно надуманный пример. Я использую только Observable, потому что делать что-то подобное не так уж и сложно. Очевидно, есть лучшие способы создать эту наблюдаемую, я только пытаюсь продемонстрировать точку. Обычно код, подписанный на наблюдаемое, выполняется асинхронно и, возможно, в другом потоке. Если использовать «foreach», это может привести к очень странным и потенциально недетерминированным результатам.
Проходит следующий тест с использованием метода расширения «ForEach»:
[Test]
public void ForEachExtensionWin()
{
//Yes, I know there is an Observable.Range.
var values = Enumerable.Range(0, 10);
var observable = Observable.Create<Func<int>>(source =>
{
values.ForEach(value =>
source.OnNext(() => value));
source.OnCompleted();
return () => { };
});
//Simulate subscribing and evaluating Funcs
var evaluatedObservable = observable.ToEnumerable().Select(func => func()).ToList();
//Win
Assert.That(evaluatedObservable,
Is.EquivalentTo(values.ToList()));
}
С ошибкой происходит следующее:
Ожидается: эквивалентно <0, 1, 2, 3, 4, 5, 6, 7, 8, 9>
Но было: <9, 9, 9, 9, 9, 9, 9, 9, 9, 9>
[Test]
public void ForEachKeywordFail()
{
//Yes, I know there is an Observable.Range.
var values = Enumerable.Range(0, 10);
var observable = Observable.Create<Func<int>>(source =>
{
foreach (var value in values)
{
//If you have resharper, notice the warning
source.OnNext(() => value);
}
source.OnCompleted();
return () => { };
});
//Simulate subscribing and evaluating Funcs
var evaluatedObservable = observable.ToEnumerable().Select(func => func()).ToList();
//Fail
Assert.That(evaluatedObservable,
Is.EquivalentTo(values.ToList()));
}