Оновлення 17.07.2012: Мабуть, станом на C # 5.0, поведінка foreach
описаної нижче змінилася, і " використання foreach
змінної ітерації в вкладеному лямбда-виразі більше не дає несподіваних результатів ". Ця відповідь не стосується C # ≥ 5.0 .
@John Skeet і всі, хто віддає перевагу ключовому слову 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);
}
}
}
Вибачення за надмірно надуманий приклад. Я використовую лише спостережуване, тому що робити щось подібне не зовсім далеко. Очевидно, що є кращі способи створити це спостережуване, я лише намагаюся продемонструвати точку. Зазвичай код, підписаний на спостережуване, виконується асинхронно і потенційно в іншій потоці. Якщо використовувати "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()));
}
ForEach()
.