.Net 闭包理解
.Net 闭包理解#
这个东西初看是比较难懂,但是一旦理解之后就很容易了,做笔记以加深印象。且看这题
example.1
class Program
{
static void Main(string[] args)
{
var actions = new List<Action>();
for (int i = 0; i < 5; i++)
{
actions.Add(() =>
{
Print(i);
});
}
actions.ForEach(x => x());
}
private static void Print(int i)
{
Console.WriteLine(i);
}
}
运行一下,结果都是5,Why?首先我们都知道,程序执行一个函数的时候,会将这个函数中用到的局部
变量压入一个栈中,退出这个函数的时候,会将栈中的数据清空。上面的代码,第一个Print函数用的i
值应该是第一个i(也就是0)所在地址的值,但是自加之后,地址变了,那么第一个Print函数用的i(也就是0)去
哪里了?我理解的话应该就是变成了一个不安全的地址,访问这个值就会出现问题。为了避免这种情况
的发生,.Net是如何处理的呢,那就是在所有Print函数执行的时候,不让这个i被清理,于是提升了i的
作用域(这个现象就叫闭包),我换个写法
example.2
class Program
{
private static int _i;
static void Main(string[] args)
{
var actions = new List<Action>();
for (_i = 0; _i < 5; _i++)
{
actions.Add(() =>
{
Print(_i);
});
}
actions.ForEach(x => x());
}
private static void Print(int i)
{
Console.WriteLine(i);
}
}
_注意:_此时是不存在闭包现象的,当然也别以为.Net就是这么做的,并不是。通过IL反汇编工具查看
example.1的代码,发现它把i封装到了一个匿名的类中,可以这么想,要使一个东西在栈上不被清理,
那就把它放到堆上...,于是用了个匿名类把i当成其属性,代码如下:
class Program
{
static void Main(string[] args)
{
var actions = new List<Action>();
var myClass = new MyClass();
for (var i = 0; i < 5; i++)
{
myClass.i = i;
actions.Add(() =>
{
Print(myClass);
});
}
myClass.i++;
actions.ForEach(x => x());
}
private static void Print(MyClass myClass)
{
Console.WriteLine(myClass.i);
}
}
internal class MyClass
{
public int i
{
get; set;
}
}
如果我们要避免这种情况,该如何做?那就相当于上诉代码每一个循环使用一个新的MyClass
class Program
{
static void Main(string[] args)
{
var actions = new List<Action>();
for (var i = 0; i < 5; i++)
{
var myClass = new MyClass();
myClass.i = i;
actions.Add(() =>
{
Print(myClass);
});
}
actions.ForEach(x => x());
}
private static void Print(MyClass myClass)
{
Console.WriteLine(myClass.i);
}
}
internal class MyClass
{
public int i
{
get; set;
}
}
其实申明MyClass这一步也可以省了,就是如下
class Program
{
static void Main(string[] args)
{
var actions = new List<Action>();
for (var i = 0; i < 5; i++)
{
var i1 = i;
actions.Add(() =>
{
Print(i1);
});
}
actions.ForEach(x => x());
}
private static void Print(int i)
{
Console.WriteLine(i);
}
}
其实上面这个写法是Resharper提示闭包之后给出的解决方案。
以上,就是我看了别人的一些理解,再加上自己的一些理解推敲出来的东西,有许多地方是模糊的,表面的,但
闭包这东西,不理解好像也没什么影响,这里起个抛砖引玉的作用,欢迎斧正拍砖。
.Net 闭包理解的更多相关文章
- javascript闭包理解
//闭包理解一 function superFun(){ var _super_a='a'; function subfuc(){ console.log(_super_a); } return su ...
- JavaScript中的闭包理解
原创文章,转载请注明:JavaScript中的闭包理解 By Lucio.Yang 1.JavaScript闭包 在小学期开发项目的时候,用node.js开发了服务器,过程中遇到了node.js的第 ...
- javascript之闭包理解以及应用场景
半个月没写博文了,最近一直在弄小程序,感觉也没啥好写的. 之前读了js权威指南,也写了篇博文,但是实话实说当初看闭包确实还是一头雾水.现在时隔一个多月(当然这一段时间还是一直有在看闭包的相关知识)理解 ...
- JavaScript闭包理解【关键字:普通函数、闭包、解决获取元素标签索引】
以前总觉得闭包很抽象,很难理解,所以百度一下"闭包"概览,百度的解释是:“闭包是指可以包含自由(未绑定到特定对象)变量的代码块:这些变量不是在这个代码块内或者任何全局上下文中定义的 ...
- JavaScript ——闭包理解
昨天晚上听别人谈起闭包这个东西,虽然对js有一点了解但却丝毫没有印象,今天也没什么事就顺便研究了一下满足好奇宝宝.整合于网上的理解,记录一下. 一.闭包的作用域 要理解闭包,首先必须理解Javascr ...
- js闭包理解实例小结
Js闭包 闭包前要了解的知识 1. 函数作用域 (1).Js语言特殊之处在于函数内部可以直接读取全局变量 <script type="text/javascript"> ...
- 对于 Javascript 闭包理解
一.变量的作用域 要理解闭包,首先必须理解Javascript特殊的变量作用域. 变量的作用域无非就是两种:全局变量和局部变量. Javascript语言的特殊之处,就在于函数内部可以直接读取全局变量 ...
- JS闭包理解_摘
原文地址1:http://www.cnblogs.com/mzwr1982/archive/2012/05/20/2509295.html 闭包是一个比较抽象的概念,尤其是对js新手来说.书上的解释实 ...
- js难点之闭包理解
如何从外部读取局部变量? 闭包就是能够读取其他函数内部变量的函数. 由于在Javascript语言中,只有函数内部的子函数才能读取局部变量,因此可以把闭包简单理解成“定义在一个函数内部的函数”. 所以 ...
随机推荐
- [Cordova] Plugin里使用iOS Framework
[Cordova] Plugin里使用iOS Framework 前言 开发Cordova Plugin的时候,在Native Code里使用第三方Library,除了可以加速项目的时程.也避免了重复 ...
- Javascript实现格式化输出
前两天看面试题,其中有一道要实现js的格式化输出,具体给出的是: Javascript实现格式化输出,比如输入999999999,输出为999,999,999 我的实现方式是 function for ...
- Android常用英文词汇不为命名纠结
ANR (Application Not Response ) bundle 捆, entire 整个的,完整的 lifetime 生命周期 entire lifetime 完整生命周期 ...
- MVC学习系列1--什么是MVC
上面的虚线表示:被动角色.实线表示:主动角色. 1.控制器和视图:控制器和视图是双向的关系,但控制器的关系更主动. 当控制器是主动的角色的时候,控制器决定要显示哪一个View:当视图为主动角色时,视图 ...
- Ubuntu如何选择更新源
刚装上Ubuntu, 决定先更新一下源. 虽然网上搜索提供了很多更新源,结果替换上实际使用的时候,却发现总是有404无法连接的情况. 后来查查资料,发现Ubuntu自己就提供了很多的源管理. 具体更新 ...
- asp.net signalR 专题—— 第四篇 模拟RPC模式的Hub操作
在之前的文章中,我们使用的都是持久连接,但是使用持久连接的话,这种模拟socket的形式使用起来还是很不方便的,比如只有一个唯一的 OnReceived方法来处理业务逻辑,如下图: protected ...
- TCP首部解析
TCP首部: TCP数据被封装在一个IP数据报中,如下: TCP首部数据格式: 16位源都口号,16为目的端口号用于寻找发送端和接收端的应用进程,加上IP首部的源端IP及终端IP,唯一的确认一个TCP ...
- JVM之Parallel Old收集器
Parallel Scavenge的老年代版本 标记-整理算法 注重吞吐量及cpu资源敏感环境.
- SQL Server 2008 R2——统计各部门某年入职人数
=================================版权声明================================= 版权声明:原创文章 谢绝转载 请通过右侧公告中的“联系邮 ...
- MongoDBV3.0.7版本(shard+replica)集群的搭建及验证
集群的模块介绍: 从MongoDB官方给的集群架构了解,整个集群主要有4个模块:Config Server.mongs. shard.replica set: Config Server:用来存放集群 ...