C#-外部变量陷阱

外变量陷阱到底是什么?感谢C#中的解释和示例。

编辑:合并乔恩·斯基特的diktat :)

外部可变陷阱上的Eric Lippert

GilliVilla asked 2020-06-30T03:18:15Z
5个解决方案
65 votes

当开发人员期望变量值由lambda表达式或匿名委托捕获,而实际上是变量本身被捕获时,就会发生“外部变量陷阱”。

例:

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();
}

有关更多详细信息,另请参见Eric Lippert的博客。

dtb answered 2020-06-30T03:19:04Z
4 votes

就像是

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

由于不会针对每次迭代执行Contains,因此不会给出您期望的结果。 不过,将s分配给循环内的临时变量将解决此问题。

heisenberg answered 2020-06-30T03:19:28Z
2 votes

值得注意的是,此陷阱也存在于for循环中,但自C#5.0起已更改,即在100 110 120循环内部,闭包现在每次都在循环变量的新副本上关闭。 所以下面的代码:

var values = new List<int>() { 100, 110, 120 };
var funcs = new List<Func<int>>();
foreach (var v in values)
    funcs.Add(() => v);
foreach (var f in funcs)
    Console.WriteLine(f());

打印for <C#5.0,但100 110 120> = C#5.0

但是,for循环的行为仍相同。

Saeb Amini answered 2020-06-30T03:19:57Z
1 votes

@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将警告“访问修改后的闭包”,在这种情况下可以安全地忽略它。

TrueWill answered 2020-06-30T03:20:26Z
0 votes

本文解释闭包的概念很有帮助:

[HTTP://恩.Wikipedia.org/wiki/closure_(computer_science)]

另外,本文从更具体的C#实现中真的很不错:

[HTTP://blogs.MSDN.com/不/AB Hi那把/archive/2005/08/08/448939.aspx]

无论如何,tl; lr是变量范围在匿名委托或lambda表达式中与在代码中的其他任何地方一样重要—行为并不那么明显。

Heather answered 2020-06-30T03:21:04Z
translate from https://stackoverflow.com:/questions/3416758/outer-variable-trap