Внешнее переменное прерывание

Каково точно Внешнее Переменное Прерывание? Объяснение и примеры в C# ценятся.

Править: Слияние диктата Jon Skeet :)

Eric Lippert на внешнем переменном прерывании

46
задан GilliVilla 5 August 2010 в 16:31
поделиться

4 ответа

"Ловушка внешней переменной" возникает, когда разработчик ожидает, что значение переменной будет захвачено лямбда-выражением или анонимным делегатом, когда на самом деле переменная захватывается сама.

Пример:

var actions = new List<Action>();
for (var i = 0; i < 10; i++)
{
    actions.Add(() => Console.Write("{0} ", i));
}
foreach (var action in actions)
{
    action();
}

Возможный вывод #1:

0 1 2 3 4 5 6 7 8 9

Возможный вывод #2:

10 10 10 10 10 10 10 10 10 10

Если вы ожидали вывод #1, вы попали в ловушку внешней переменной. Вы получите вывод №2.

Исправление:

Объявите "Внутреннюю переменную" для многократного захвата вместо "Внешней переменной", которая захватывается только один раз.

var actions = new List<Action>();
for (var i = 0; i < 10; i++)
{
    var j = i;
    actions.Add(() => Console.Write("{0} ", j));
}
foreach (var action in actions)
{
    action();
}

Более подробно см. также блог Эрика Липперта.

65
ответ дан 26 November 2019 в 20:32
поделиться

Что-то вроде

foreach (var s in strings)
    var x = results.Where(r => (r.Text).Contains(s));

не даст ожидаемых результатов, потому что Contains не выполняется для каждой итерации. Однако присвоение s временной переменной внутри цикла исправит это.

4
ответ дан 26 November 2019 в 20:32
поделиться

@dtb правильный (большой +1), но важно отметить, что это применимо, только если область закрытия выходит за пределы петли. Например:

var objects = new []
    {
        new { Name = "Bill", Id = 1 },
        new { Name = "Bob", Id = 5 },
        new { Name = "David", Id = 9 }
    };

for (var i = 0; i < 10; i++)
{
    var match = objects.SingleOrDefault(x => x.Id == i);

    if (match != null)
    {
        Console.WriteLine("i: {0}  match: {1}", i, match.Name);
    }
}

Будет напечатано:

i: 1  match: Bill
i: 5  match: Bob
i: 9  match: David

ReSharper предупредит о «Доступе к измененному закрытию», которое в этом случае можно игнорировать.

1
ответ дан 26 November 2019 в 20:32
поделиться

Эта статья, объясняющая концепцию замыканий, полезна:

http://en.wikipedia.org/wiki/Closure_(computer_science)

Также эта статья очень хороша с более конкретной реализации C#:

http://blogs. msdn.com/b/abhinaba/archive/2005/08/08/448939.aspx

В любом случае, суть в том, что область видимости переменных так же важна в анонимных делегатах или лямбда-выражениях, как и в любом другом месте вашего кода - поведение просто не так очевидно.

0
ответ дан 26 November 2019 в 20:32
поделиться
Другие вопросы по тегам:

Похожие вопросы: