.NET:C# 如何实现的闭包?
背景
C# 在编译器层面为我们提供了闭包机制(Java7 和 Go 也是这种思路),本文简单的做个解释。
背景知识
你必须了解:引用类型、值类型、引用、对象、值类型的值(简称值)。
关于引用、对象和值在内存的分配有如下几点规则:
- 对象分配在堆中。
- 作为字段的引用分配在堆中(内嵌在对象中)。
- 作为局部变量(参数也是局部变量)的引用分配在栈中。
- 作为字段的值分配在堆中(内嵌在对象中)。
- 作为局部变量(参数也是局部变量)的值用分配在栈中。
- 局部变量只能存活于所在的作用域(方法中的大括号确定了作用域的长短)。
注:按值传递和按引用传递也是需要掌握的知识点,C# 默认是按值传递的。
闭包示例
测试代码
private static void Before()
{
Action[] actions = new Action[]; for (var i = ; i < actions.Length; i++)
{
actions[i] = () =>
{
Console.WriteLine(i);
};
} foreach (var item in actions)
{
item();
}
}
输出结果

编译器帮我们做了是什么?
编译器帮我们生成的代码(我自己写的,可以使用 Reflector 工具自己查看)
private static void After()
{
Action[] actions = new Action[]; var anonymous = new AnonymousClass(); for (anonymous.i = ; anonymous.i < actions.Length; anonymous.i++)
{
actions[anonymous.i ] = anonymous.Action;
} foreach (var item in actions)
{
item();
}
} class AnonymousClass
{
public int i; public void Action()
{
Console.WriteLine(this.i);
}
}
如何修复上面的问题?
上面的例子不是我们期望的输出,让我们给出两种修改方案:
第一种(借鉴JS)
private static void Fix()
{
Action[] actions = new Action[]; for (var i = ; i < actions.Length; i++)
{
new Action<int>((j) =>
{
actions[i] = () =>
{
Console.WriteLine(j);
};
})(i);
} foreach (var item in actions)
{
item();
}
}
第二种
public static void Fix2()
{
Action[] actions = new Action[]; for (var i = ; i < actions.Length; i++)
{
var j = i; actions[i] = () =>
{
Console.WriteLine(j);
};
} foreach (var item in actions)
{
item();
}
}
分析
编译器将闭包引用的局部变量转换为匿名类型的字段,导致了局部变量分配在堆中。
备注
C# 编译器帮我们做了非常多的工作,如:自动属性、类型推断、匿名类型、匿名委托、Lamda 表达式、析构方法、await 和 sync、using、对象初始化表达式、lock、默认参数 等等,这些统称为“语法糖”。
.NET:C# 如何实现的闭包?的更多相关文章
- 《Web 前端面试指南》1、JavaScript 闭包深入浅出
闭包是什么? 闭包是内部函数可以访问外部函数的变量.它可以访问三个作用域:首先可以访问自己的作用域(也就是定义在大括号内的变量),它也能访问外部函数的变量,和它能访问全局变量. 内部函数不仅可以访问外 ...
- 干货分享:让你分分钟学会 JS 闭包
闭包,是 Javascript 比较重要的一个概念,对于初学者来讲,闭包是一个特别抽象的概念,特别是ECMA规范给的定义,如果没有实战经验,很难从定义去理解它.因此,本文不会对闭包的概念进行大篇幅描述 ...
- 深入浅出JavaScript之闭包(Closure)
闭包(closure)是掌握Javascript从人门到深入一个非常重要的门槛,它是Javascript语言的一个难点,也是它的特色,很多高级应用都要依靠闭包实现.下面写下我的学习笔记~ 闭包-无处不 ...
- javascript之闭包理解以及应用场景
半个月没写博文了,最近一直在弄小程序,感觉也没啥好写的. 之前读了js权威指南,也写了篇博文,但是实话实说当初看闭包确实还是一头雾水.现在时隔一个多月(当然这一段时间还是一直有在看闭包的相关知识)理解 ...
- js闭包 和 prototype
function test(){ var p=200; function q(){ return p++; } return q; } var s = test(); alert(s()); aler ...
- js闭包for循环总是只执行最后一个值得解决方法
<style> li{ list-style: none;width:40px;height: 40px;text-align:center;line-height: 40px;curso ...
- JavaScript学习笔记(二)——闭包、IIFE、apply、函数与对象
一.闭包(Closure) 1.1.闭包相关的问题 请在页面中放10个div,每个div中放入字母a-j,当点击每一个div时显示索引号,如第1个div显示0,第10个显示9:方法:找到所有的div, ...
- 带你一分钟理解闭包--js面向对象编程
上一篇<简单粗暴地理解js原型链--js面向对象编程>没想到能攒到这么多赞,实属意外.分享是个好事情,尤其是分享自己的学习感悟.所以网上关于原型链.闭包.作用域等文章多如牛毛,很多文章写得 ...
- 如何设计一门语言(七)——闭包、lambda和interface
人们都很喜欢讨论闭包这个概念.其实这个概念对于写代码来讲一点用都没有,写代码只需要掌握好lambda表达式和class+interface的语义就行了.基本上只有在写编译器和虚拟机的时候才需要管什么是 ...
- JavaScript 闭包深入浅出
闭包是什么? 闭包是内部函数可以访问外部函数的变量.它可以访问三个作用域:首先可以访问自己的作用域(也就是定义在大括号内的变量),它也能访问外部函数的变量,和它能访问全局变量. 内部函数不仅可以访问外 ...
随机推荐
- 1.RapidIO协议概述
转自https://www.cnblogs.com/liujinggang/p/9925859.html 一.RapidIO背景介绍 RapidIO是由Motorola和Mercury等公司率先倡导的 ...
- 自己动手写把”锁”---LockSupport深入浅出
本篇是<自己动手写把"锁">系列技术铺垫的最后一个知识点.本篇主要讲解LockSupport工具类,它用来实现线程的挂起和唤醒. LockSupport是Java6引入 ...
- 手机APP自动化之uiautomator2 +python3 UI自动化
题记: 之前一直用APPium直到用安卓9.0 发现uiautomatorviewer不支持安卓 9.0,点击截屏按钮 一直报错,百度很久解决方法都不可以,偶然间看见有人推荐:uiautomator ...
- Linux下防御DDOS攻击的操作梳理
DDOS的全称是Distributed Denial of Service,即"分布式拒绝服务攻击",是指击者利用大量“肉鸡”对攻击目标发动大量的正常或非正常请求.耗尽目标主机资源 ...
- C_数据结构_走迷宫
#include <stdio.h> #include <conio.h> #include <windows.h> #include <time.h> ...
- 《Linux内核分析》第13章
<Linux内核设计与实现>第十三章--虚拟文件系统概述 20135211 一.通用文件系统接口 之所以可以使用这种通用接口(VF)对所有类型的文件系统进行操作,是因为内核在它的底层文件系 ...
- bate版说明书
本游戏是一款手机游戏,学生可以在无聊时打发时间,放松心情.现在只有十关,游戏运行还算可以. 特点: 对alpha版进行了修改,可以进行暂停,重开,返回目录. 画面也进行了优化,不象之前的那么粗超. 游 ...
- 作业1+2.四则运算(改进后完整版,用python写的)_064121陶源
概述: 用一个星期加上五一的三天假期自学了python,在Mac系统上重新写出了四则运算的程序,编译器是PyCharm,相当于完成了作业2.d)"选一个你从来没有学过的编程语言,试一试实现基 ...
- ios开发之--CAKeyframeAnimation的详细用法
简单的创建一个带路径的动画效果,比较粗糙,不过事先原理都是一样的, 代码如下: 1,创建动画所需的view -(void)creatView { moveView = [UIView new]; mo ...
- Java 常用类的使用例子(整理)
可变字符序列——StringBuffer StringBuffer类和String类的方法几乎一样,不过StringBuffer对象表示的字符串是可以改变的,而String对象保存的字符串是不可变的. ...