整理贴,NET中的闭包《续》:闭包造成的意外。
2016-05-19 10:38
225 查看
http://blog.csdn.net/yan_hyz/article/details/7799672
闭包是将一些执行封装,将它像对象一样传递,在传递时,执行依然能够访问到原上下文。访问原来上下文,是闭包的重要特征
代码:
[csharp] view
plain copy
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
改进:
[csharp] view
plain copy
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)的对象中。
提升之后,即使创建该变量的方法执行完毕该变量仍不会消亡。
当指向该匿名函数的所有引用都消失后,该闭包变量即可正常地被垃圾回收器回收
[csharp] view
plain copy
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]
闭包是将一些执行封装,将它像对象一样传递,在传递时,执行依然能够访问到原上下文。访问原来上下文,是闭包的重要特征
代码:
[csharp] view
plain copy
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
改进:
[csharp] view
plain copy
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)的对象中。
提升之后,即使创建该变量的方法执行完毕该变量仍不会消亡。
当指向该匿名函数的所有引用都消失后,该闭包变量即可正常地被垃圾回收器回收
[csharp] view
plain copy
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]
相关文章推荐
- 服务器同步工具 sersync
- iOS 自定义UITabBar
- Hibernate pojo对象的三种状态
- 关于STM32 MDK中USE_STDPERIPH_DRIVER问题及 STM32F10X_LD STM32F10X_MD STM32F10X_HD STM32F10X_CL宏定义的选择
- 自定义Android Toast样式
- 树的遍历
- js计算百分比
- Android使用最小宽度限定符时最小宽度的计算
- GoAhead2.5源代码分析之2-文件数据库(emfdb.c)
- 安卓代码混淆技术分享
- 进程间通信之XSI IPC
- 页面+html5+css3
- Linux 传输文件
- python开发目录合并小工具 PathMerge
- ExtJs Column 显示文字内容过长 使用Tootip显示全部内容
- 个人学习-java-泛型理解
- C/C++中strstr()函数:返回字符串中首次出现子串的地址
- Java后台,安卓,iOS--3des解密后得到密文不同,解决方法
- C++ 什么是继承和派生
- android 开源框架