整理贴,NET中的闭包《续》:闭包造成的意外。

闭包是将一些执行封装,将它像对象一样传递,在传递时,执行依然能够访问到原上下文。访问原来上下文,是闭包的重要特征

 

代码:

static void Main(string[] args)

{

List<Action> ls = new List<Action>();

for (int i = 0; i < 10; i++)

{

ls.Add(() => Console.WriteLine(i));

}

foreach (Action action in ls)

{

action();

}

System.Console.Read();

}

结果:

原因:

由于只声明了一个i变量

所以所有的Action捕获的都是同一个i变量。结果就是每一行都输出数字10


改进:


static void Main(string[] args)

{

List<Action> ls = new List<Action>();

for (int i = 0; i < 10; i++)

{

int tp = i;

ls.Add(() => Console.WriteLine(tp));

}

foreach (Action action in ls)

{

action();

}

System.Console.Read();

}

与上例代码的唯一不是是在循环体中使用了一个局部变量tp,这种写法在通常看来不通是多用了一个中转变量,对程的执行不会有什么影响,但事实上tp这个变量在被每个Action独立保存.

这样,每次循环体在执行的时候,都会取得一个全新的tp,而且tp不会因为所在声名体的完成而出栈




若匿名方法中如果引用了某个变量,则该局部变量将被提升为实例变量,并储存于一个叫做闭包(closure)的对象中。

提升之后,即使创建该变量的方法执行完毕该变量仍不会消亡。

当指向该匿名函数的所有引用都消失后,该闭包变量即可正常地被垃圾回收器回收

class Program

{

delegate int wxd(int i);

delegate wxd lzm(int ii);

static void Main(string[] args)

{

lzm obj = delegate(int ii)

{

return

delegate(int i)

{

return i + ii;

};

};

wxd w1 = obj(1);

wxd w2 = obj(2);

System.Console.WriteLine(w1(3));

System.Console.WriteLine(w2(3));

System.Console.Read();

}

}

输出的结果是4和5

[obj]函数接受一个参数,返回新的函数[w1,w2]。新的函数[w1,w2]将[obj]的参数与自己的参数相加,返回结果

函数[w1,w2]在接受相同的参数的时候,产生了不同的结果。

实际上[obj]返回的内部函数已经把[obj]的参数[ii]记录了

在这里,方法已不仅仅是一个函数指针了

[obj]的参数[ii]是如何记录的:

通常理解,函数的参数是放在栈中的。

如果闭包也将参数放在栈中,那么[ii]在[obj]运行结束的时候就会消失掉,这个时候[w1,w2]再通过栈去搜索[ii]显然就是不可能的。

所以闭包中参数或内部变量不能放在栈中.而是放在程序执行过程之中的一张全局表里.

[[obj]在返回内部函数的时候,将全局表,自己的结构表,内部函数的指针一起传递给变量[w1,w2].

这时内部函数可以访问[ii],外部却无法访问[ii]



原文链接:

点击打开链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值