Редактировать: это все изменения в C # 5, с изменением места определения переменной (в глазах компилятора). С C # 5 и далее они одинаковые .
До C # 5
Второе безопасно; первое не.
При foreach
переменная объявляется вне цикла - т.е.
Foo f;
while(iterator.MoveNext())
{
f = iterator.Current;
// do something with f
}
Это означает, что существует только 1 f
с точки зрения области закрытия, и потоки могут очень запутаться - вызывая метод несколько раз в одних случаях, а в других - нет. Это можно исправить с помощью второго объявления переменной внутри цикла:
foreach(Foo f in ...) {
Foo tmp = f;
// do something with tmp
}
В этом случае в каждой области замыкания есть отдельный tmp
, поэтому риска возникновения этой проблемы нет.
Вот простое доказательство проблемы:
static void Main()
{
int[] data = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
foreach (int i in data)
{
new Thread(() => Console.WriteLine(i)).Start();
}
Console.ReadLine();
}
Выходы (случайным образом):
1
3
4
4
5
7
7
8
9
9
Добавить временную переменную, и она работает:
foreach (int i in data)
{
int j = i;
new Thread(() => Console.WriteLine(j)).Start();
}
(каждый номер один раз, но, конечно, порядок не гарантируется)