闭包(Closure)是词法闭包(Lexical Closure)的简称。对闭包的具体定义有很多种说法,这些说法大体可以分为两类:

一种说法认为闭包是符合一定条件的函数,比如参考资源中这样定义闭包:闭包是在其词法上下文中引用了自由变量(注1)的函数。
另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。比如参考资源中就有这样的的定义:在实现深约束(注2)时,需要创建一个能显式表示引用环境的东西,并将它与相关的子程序捆绑在一起,这样捆绑起来的整体被称为闭包。
这两种定义在某种意义上是对立的,一个认为闭包是函数,另一个认为闭包是函数和引用环境组成的整体。虽然有些咬文嚼字,但可以肯定第二种说法更确切。闭包只是在形式和表现上像函数,但实际上不是函数。函数是一些可执行的代码,这些代码在函数被定义后就确定了,不会在执行时发生变化,所以一个函数只有一个实例。闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。所谓引用环境是指在程序执行中的某个点所有处于活跃状态的约束所组成的集合。其中的约束是指一个变量的名字和其所代表的对象之间的联系。那么为什么要把引用环境与函数组合起来呢?这主要是因为在支持嵌套作用域的语言中,有时不能简单直接地确定函数的引用环境。这样的语言一般具有这样的特性:

函数是一阶值(First-class value),即函数可以作为另一个函数的返回值或参数,还可以作为一个变量的值。
函数可以嵌套定义,即在一个函数内部可以定义另一个函数。

在C#里面,变量作用域有三种,一种是属于类的,我们常称之为field;第二种则属于函数的,我们通常称之为局部变量;还有一种,其实也是属于函数的,不过它的作用范围更小,它只属于函数局部的代码片段,这种同样称之为局部变量。这三种变量的生命周期基本都可以用一句话来说明,每个变量都属于它所寄存的对象,即变量随着其寄存对象生而生和消亡。对应三种作用域我们可以这样说,类里面的变量是随着类的实例化而生,同时伴随着类对象的资源回收而消亡(当然这里不包括非实例化的static和const对象)。而函数(或代码片段)的变量也随着函数(或代码片段)调用开始而生,伴随函数(或代码片段)调用结束而自动由GC释放,它内部变量生命周期满足先进后出的特性。

比如说下面这个闭包例子:

public class ClosureTest
{
public static Func<int> GetClosure()
{
var n = ;
return () =>
{
n++;
return n;
};
} public static void Main()
{
Func<int> fn = null;
fn = GetClosure();
Console.WriteLine(fn());
Console.WriteLine(fn());
Console.WriteLine(fn());
}
}

执行的结果是
1
2
3
而不是别的。

可以看出局部变量n并没有在函数调用后被回收,而是一直存在。
我们这里就讲解怎么实现闭包。
在最后的生成二进制文件中,局部变量n已经不是在函数内,而是转移到了一个内部类中。

一、生成内部类
这个内部类有一下几个成员
1.一个字段,用于存储n的值;
2.一个最简单的构造函数,用于被调用生成实例;
3.一个方法,用于执行lamda表达式。

生成内部类的
第1步:声明内部类

nestedTypeBuilder = typeBuilder.DefineNestedType("ClosureNestedClass", TypeAttributes.NestedPublic | TypeAttributes.Sealed);

第2步:创建字段
字段的类型和局部变量n的类型是一样的。

nestedFieldBuilder = nestedTypeBuilder.DefineField("NestedFeld", typeof(int), FieldAttributes.Public);

第3步:构造函数
构造函数是一个无参的构造函数。

nestedNewBuilder = nestedTypeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new Type[] { });

它的方法体只需要一个最简单ret指令就可以了。

var il = nestedNewBuilder.GetILGenerator();
il.Emit(OpCodes.Ret);

第4步:创建字段
方法是无参的,返回类型和局部变量n的类型一样。

nestedLambdsMethod = nestedTypeBuilder.DefineMethod("Run", MethodAttributes.Public, typeof(int), new Type[] { });

方法体内声明一个局部变量用于返回结果。

LocalBuilder retlocalBuilder = ilGenerator.DeclareLocal(typeof(int));

按照上面的示例程序给字段n++。

ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Ldfld, nestedFieldBuilder);
ilGenerator.Emit(OpCodes.Ldc_I4_1);
ilGenerator.Emit(OpCodes.Add);
ilGenerator.Emit(OpCodes.Stfld, nestedFieldBuilder);

把字段保存到那个局部变量上

ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Ldfld, nestedFieldBuilder);
ilGenerator.Emit(OpCodes.Stloc_0);

返回结果

ilGenerator.Emit(OpCodes.Ldloc_0);
ilGenerator.Emit(OpCodes.Ret);

第5步:完成类型

nestedTypeBuilder.CreateType();

二、生成GetClosure方法

1.声明方法

getClosureMethod = typeBuilder.DefineMethod("GetClosure", MethodAttributes.Public
| MethodAttributes.Static, typeof(Func<int>), new Type[] { });

2.声明两个局部变量
用于暂存
LocalBuilder localBuilder0 = ilGenerator.DeclareLocal(nestedTypeBuilder);
用于返回结果

LocalBuilder retlocalBuilder = ilGenerator.DeclareLocal(typeof(Func<int>));

3.创建一个内部类实例,保存到localBuilder0

ilGenerator.Emit(OpCodes.Newobj, nestedNewBuilder);
ilGenerator.Emit(OpCodes.Stloc_0);

4.给这个实例的字段初始化为0
这里是对应示例程序
var n = 0;

ilGenerator.Emit(OpCodes.Ldloc_0);
ilGenerator.Emit(OpCodes.Ldc_I4_0);
ilGenerator.Emit(OpCodes.Stfld, nestedFieldBuilder);

5.生成一个Func<int>实例,并保存
这里用Ldftn指令

ilGenerator.Emit(OpCodes.Ldloc_0);
ilGenerator.Emit(OpCodes.Ldftn, nestedLambdsMethod);
ilGenerator.Emit(OpCodes.Newobj, typeof(Func<int>).GetConstructors()[]);
ilGenerator.Emit(OpCodes.Stloc_1);

6.返回结果

ilGenerator.Emit(OpCodes.Ldloc_1);
ilGenerator.Emit(OpCodes.Ret);

三、生成测试方法
这个比较简单,不是本篇重点。

        static void GenerateMain()
{
mainMethod = typeBuilder.DefineMethod("Main", MethodAttributes.Public
| MethodAttributes.Static, typeof(void), new Type[] { });
ILGenerator ilGenerator = mainMethod.GetILGenerator(); LocalBuilder localBuilder0 = ilGenerator.DeclareLocal(typeof(Func<int>)); ilGenerator.Emit(OpCodes.Call, getClosureMethod);
ilGenerator.Emit(OpCodes.Stloc_0); ilGenerator.Emit(OpCodes.Ldloc_0);
ilGenerator.Emit(OpCodes.Callvirt, typeof(Func<int>).GetMethod("Invoke", new Type[] { }));
ilGenerator.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(int)})); ilGenerator.Emit(OpCodes.Ldloc_0);
ilGenerator.Emit(OpCodes.Callvirt, typeof(Func<int>).GetMethod("Invoke", new Type[] { }));
ilGenerator.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(int) })); ilGenerator.Emit(OpCodes.Ldloc_0);
ilGenerator.Emit(OpCodes.Callvirt, typeof(Func<int>).GetMethod("Invoke", new Type[] { }));
ilGenerator.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(int) })); EmitReadKey(ilGenerator);
ilGenerator.Emit(OpCodes.Ret);
}

完整的程序如下:

using System;
using System.Reflection;
using System.Reflection.Emit; namespace LX1_ILDemo
{
class Demo29_Closure
{
static string binaryName = "Demo29_Closure.exe";
static string namespaceName = "LX1_ILDemo";
static string typeName = "DemoClosure"; static AssemblyBuilder assemblyBuilder;
static ModuleBuilder moduleBuilder;
static TypeBuilder typeBuilder;
static MethodBuilder mainMethod;
static MethodBuilder getClosureMethod; static TypeBuilder nestedTypeBuilder;
static FieldBuilder nestedFieldBuilder;
static MethodBuilder nestedLambdsMethod;
static ConstructorBuilder nestedNewBuilder; public static void Generate()
{
InitAssembly();
typeBuilder = moduleBuilder.DefineType(namespaceName + "." + typeName, TypeAttributes.Public); Generate_Nested();
Generate_GetClosure();
GenerateMain(); assemblyBuilder.SetEntryPoint(mainMethod, PEFileKinds.ConsoleApplication);
SaveAssembly();
Console.WriteLine("生成成功");
} static void Generate_Nested()
{
nestedTypeBuilder = typeBuilder.DefineNestedType("ClosureNestedClass", TypeAttributes.NestedPublic | TypeAttributes.Sealed);
nestedFieldBuilder = nestedTypeBuilder.DefineField("NestedFeld", typeof(int), FieldAttributes.Public);
nestedLambdsMethod = nestedTypeBuilder.DefineMethod("Run", MethodAttributes.Public, typeof(int), new Type[] { });
nestedNewBuilder = nestedTypeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new Type[] { });
var il = nestedNewBuilder.GetILGenerator();
il.Emit(OpCodes.Ret); ILGenerator ilGenerator = nestedLambdsMethod.GetILGenerator();
LocalBuilder retlocalBuilder = ilGenerator.DeclareLocal(typeof(int));
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Ldfld, nestedFieldBuilder);
ilGenerator.Emit(OpCodes.Ldc_I4_1);
ilGenerator.Emit(OpCodes.Add);
ilGenerator.Emit(OpCodes.Stfld, nestedFieldBuilder);
ilGenerator.Emit(OpCodes.Ldarg_0);
ilGenerator.Emit(OpCodes.Ldfld, nestedFieldBuilder);
ilGenerator.Emit(OpCodes.Stloc_0); ilGenerator.Emit(OpCodes.Ldloc_0);
ilGenerator.Emit(OpCodes.Ret); nestedTypeBuilder.CreateType();
} static void Generate_GetClosure()
{
getClosureMethod = typeBuilder.DefineMethod("GetClosure", MethodAttributes.Public
| MethodAttributes.Static, typeof(Func<int>), new Type[] { });
ILGenerator ilGenerator = getClosureMethod.GetILGenerator(); LocalBuilder localBuilder0 = ilGenerator.DeclareLocal(nestedTypeBuilder);
LocalBuilder retlocalBuilder = ilGenerator.DeclareLocal(typeof(Func<int>)); ilGenerator.Emit(OpCodes.Newobj, nestedNewBuilder);
ilGenerator.Emit(OpCodes.Stloc_0); ilGenerator.Emit(OpCodes.Ldloc_0);
ilGenerator.Emit(OpCodes.Ldc_I4_0);
ilGenerator.Emit(OpCodes.Stfld, nestedFieldBuilder); ilGenerator.Emit(OpCodes.Ldloc_0);
ilGenerator.Emit(OpCodes.Ldftn, nestedLambdsMethod);
ilGenerator.Emit(OpCodes.Newobj, typeof(Func<int>).GetConstructors()[]);
ilGenerator.Emit(OpCodes.Stloc_1); ilGenerator.Emit(OpCodes.Ldloc_1);
ilGenerator.Emit(OpCodes.Ret); } static void GenerateMain()
{
mainMethod = typeBuilder.DefineMethod("Main", MethodAttributes.Public
| MethodAttributes.Static, typeof(void), new Type[] { });
ILGenerator ilGenerator = mainMethod.GetILGenerator(); LocalBuilder localBuilder0 = ilGenerator.DeclareLocal(typeof(Func<int>)); ilGenerator.Emit(OpCodes.Call, getClosureMethod);
ilGenerator.Emit(OpCodes.Stloc_0); ilGenerator.Emit(OpCodes.Ldloc_0);
ilGenerator.Emit(OpCodes.Callvirt, typeof(Func<int>).GetMethod("Invoke", new Type[] { }));
ilGenerator.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(int)})); ilGenerator.Emit(OpCodes.Ldloc_0);
ilGenerator.Emit(OpCodes.Callvirt, typeof(Func<int>).GetMethod("Invoke", new Type[] { }));
ilGenerator.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(int) })); ilGenerator.Emit(OpCodes.Ldloc_0);
ilGenerator.Emit(OpCodes.Callvirt, typeof(Func<int>).GetMethod("Invoke", new Type[] { }));
ilGenerator.Emit(OpCodes.Call, typeof(Console).GetMethod("WriteLine", new Type[] { typeof(int) })); EmitReadKey(ilGenerator);
ilGenerator.Emit(OpCodes.Ret);
} static void EmitReadKey(ILGenerator ilGenerator)
{
MethodInfo readKeyMethod = typeof(Console).GetMethod("ReadKey", new Type[] { });
ilGenerator.Emit(OpCodes.Call, readKeyMethod);
ilGenerator.Emit(OpCodes.Pop);
} static void InitAssembly()
{
AssemblyName assemblyName = new AssemblyName(namespaceName);
assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);
moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name, binaryName);
} static void SaveAssembly()
{
Type t = typeBuilder.CreateType(); //完成Type,这是必须的
assemblyBuilder.Save(binaryName);
}
}
}

MSIL实用指南-闭包的生成和调用的更多相关文章

  1. MSIL实用指南-Action的生成和调用

    MSIL实用指南-Action的生成和调用 System.Action用于封装一个没有参数没有返回值的方法.这里生成需要Ldftn指令. 下面讲解怎生成如下的程序. class ActionTest ...

  2. MSIL实用指南-struct的生成和操作

    struct(结构)是一种值类型,用于将一组相关的信息变量组织为一个单一的变量实体.所有的结构都继承自System.ValueType类,因此是一种值类型,也就是说,struct实例分配在线程的堆栈( ...

  3. MSIL实用指南-类相关生成

    一.创建class用MethodBuilder的DefineType方法,可以指定父类,得到一个TypeBuilder对象. 二.实现继承接口用TypeBuilder的AddInterfaceImpl ...

  4. MSIL实用指南-this的生成

    C#关键字是非静态方法体内部,用Ldarg_0指代this例子ilGenerator.Emit(OpCodes.Ldarg_0);

  5. MSIL实用指南-生成索引器

    MSIL实用指南-生成索引器 索引器是一种特殊的属性,它有参数的,也有get和set方法,属性名称一般是"Item",并且方法名称一般名称是"get_Item" ...

  6. MSIL实用指南-生成属性

    本篇讲解怎么生成属性,包括get和set方法. 第一步,生成一个字段生成字段用TypeBuilder.DefineField方法.实例代码: FieldBuilder customerNameBldr ...

  7. MSIL实用指南-生成构造函数

    本篇讲解生成构造函数的一些知识,包括创建实例构造函数.静态构造函数.调用父类构造函数. 生成构造函数的方法生成构造函数的方法是TypeBuilder.DefineConstructor(MethodA ...

  8. MSIL实用指南-生成if...else...语句

    if...else...语句是非常重要的选择语句,它的生成一般需要ILGenerator的DefineLabel方法和MarkLabel方法,以及Brtrue_S和Br_S指令. 一.DefineLa ...

  9. MSIL实用指南-生成foreach语句

    foreach可以迭代数组或者一个集合对象.foreach语句格式是它的生成步骤是foreach (<成员> in <集合>) <循环体> 一.声明三个变量,loc ...

随机推荐

  1. 【CSS】 CSS的一些应用实例和参考

    css 一些应用实例 基本抄自http://www.w3school.com.cn/css/css_align.asp ..把这些知识消化吸收然后以自己的话来解释一下 ■ 对齐 ●  用margin属 ...

  2. 后端Nodejs利用node-xlsx模块读取excel

    后端Nodejs(利用node-xlsx模块) /** * Created by zh on 16-9-14. */ var xlsx = require("node-xlsx") ...

  3. Android学习链接大放送

    虽然贴链接这种事情..真是一种很偷懒的做法... 但是我一个小菜鸟,果断还是要以多向别人学习为主... 好资源要和大家分享对不对! 况且..放博客里..比收藏夹的利用几率要大一点! 原作者应该也很喜欢 ...

  4. 涉及模式之 装饰器模式详解(与IO不解的情缘)

    作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. LZ到目前已经写了九个设计模 ...

  5. 微信公众平台开发,API接入与推送事件(1)

    博客好久没有更新了,今天说说微信开发.微信开发的好多初学者都会又这样的迷惑,微信开发到底是什么?其实微信开发本质我和我们的网站开发没有太大的区别.我们常说的微信开发也就是公众号开,微信公众号分为三个类 ...

  6. java中的IO 的示例

    字符流 package jd_1; import java.io.BufferedReader;import java.io.BufferedWriter;import java.io.FileNot ...

  7. 火狐浏览器中如何删除保存的cookie

    大致分为三步即可: 打开浏览器并查看图示,按照图示操作即可完成:

  8. C语言二维数组作业

    一.PTA实验作业 题目1:7-3 出生年 1. 本题PTA提交列表 2. 设计思路 1.声明一个函数different()用来计算一个年份的不同数字个数 2.定义y(y是来计算符合要求的年份的量), ...

  9. 我从业11年来遇到的最奇葩的raid0+1数据恢复经历

    我是一名数据恢复工程师,从事数据恢复行业已经11年了,前几天接到一组4块盘SCSI RAID0+1的数据恢复,客户说做了两组raid1,现在raid状态里显示有3快盘offline.如果两组盘分别作r ...

  10. Spring mvc中junit测试遇到com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException错误怎么解决

    今天遇到图片中的错误,纠结了一下,弄清楚了怎么从控制台中读取错误信息,并修改错误. com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: ...