为什么以下代码打印11次?
int i = 10;
Action fn1 = () => Console.WriteLine(i);
i = 11;
Action fn2 = () => Console.WriteLine(i);
fn1();
fn2();
输出 11 11
根据这篇文章中的答案 - How to tell a lambda function to capture a copy instead of a reference in C#? - 将lambda翻译成一个带有捕获变量副本的类。如果是这样,我的例子不应该打印10& 11?
现在,当lambda通过引用捕获时,它如何影响捕获的变量的生命周期。例如,假设上面的代码片段在函数&动作的范围对于变量是全局的,如下所示:
class Test
{
Action _fn1;
Action _fn2;
void setActions()
{
int i = 10;
_fn1 = () => Console.WriteLine(i);
i = 11;
_fn2 = () => Console.WriteLine(i);
}
static void Main()
{
setActions();
_fn1();
_fn2();
}
}
在这种情况下,当调用动作时,变量i不会超出范围吗?那么,这些动作是否留下悬挂指针的参考?
答案 0 :(得分:6)
如果是这种情况,我的例子不应该打印10& 11?
不,因为您只有一个变量 - fn1
会捕获变量,而不是当前的值。所以这样的方法:
static void Foo()
{
int i = 10;
Action fn1 = () => Console.WriteLine(i);
i = 11;
Action fn2 = () => Console.WriteLine(i);
fn1();
fn2();
}
翻译成这样:
class Test
{
class MainVariableHolder
{
public int i;
public void fn1() => Console.WriteLine(i);
public void fn2() => Console.WriteLine(i);
}
void Foo()
{
var holder = new MainVariableHolder();
holder.i = 10;
Action fn1 = holder.fn1;
holder.i = 11;
Action fn2 = holder.fn2;
fn1();
fn2();
}
}
这也回答了你的第二点:变量被“提升”到一个类中,所以只要委托还活着,它的生命周期就会有效地延长。
答案 1 :(得分:0)
确实生成了一个类,它具有捕获变量的字段,而不是它们的值。因此,当lamba将被执行时,运行时将检查{em>当前的i
值,即11,因为您在调用lambda之前已经更新了它。这就是你看到这个的原因。为了验证此参数,您可以重新排列语句的顺序,如下所示:
int i = 10;
Action fn1 = () => Console.WriteLine(i);
fn1();
i = 11;
Action fn2 = () => Console.WriteLine(i);
fn2();
答案 2 :(得分:-1)
由于延迟执行,它打印两次,而不是因为它捕获变量的方式。
当执行被推迟时,它将打印i的最新值,因为我是被捕获的,而不是它的值。