• MSIL实用指南-闭包的生成和调用


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

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

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


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

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

    public class ClosureTest
    {
            public static Func<int> GetClosure()
            {
                var n = 0;
                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()[0]);
    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()[0]);
                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);
            }
        }
    }
    View Code
  • 相关阅读:
    怎样让jquery mobile 的footer/header 固定?
    smarty-smarty模板中类似for循环功能的实现代码
    mysql-MySql Update与case when
    PHP-获取用户所有定义的常量
    PHP-去掉php中var_dump()函数输出的省略号,让它完整显示0.0
    PHP--判断是否为时间戳
    Chrome/360极速/猎豹/枫树/浏览器去除视频网站广告利器插件——【切糕】广告视频屏蔽专家下载
    bootstrap使用modal动态对话框时,按回车键无法确认,反而取消对话框
    【bootstrap】常用bootstrap类库插件
    Thinkphp-开发技巧
  • 原文地址:https://www.cnblogs.com/tkt2016/p/8867086.html
Copyright © 2020-2023  润新知