原 c#中闭包的实现方法

闭包即闭包是指可以包含自由(未绑定到特定对象)变量的代码块.表现出来是调用函数结束后,函数内的变量的生存周期拉长到调用者的生命。很多闭包实现成匿名函数(js也是表现成匿名函数的,其他的方法不清楚),3.0中引入了匿名函数,相应的也提供了闭包的支持。

闭包实例:

    class Program
    {
        static void Main(string[] args)
        {
            Action ss = bibao();
            ss();
            Console.ReadKey();
        }
        public static Action bibao()
        {
            int i = 100;
            return delegate {
                Console.WriteLine(i);
            };
        }
    }

通常情况下{}内的变量在结束后会结束生命周期,然而在这里ss()却可以打印出i的值。生命周期从bibao函数里面拉长到Main里面。

在js里面是通过函数对象之间作用域链的引用关系实现,那么在c#中又是用什么方法实现的呢?

反编译代码:

编译后的代码生成了一个新的类,c#的闭包就是建立在这个类的基础上面的。

其中闭包中的变量作为类的公开成员变量,闭包函数自身作为成员,类型是internal。因为此类和闭包函数所在的类生成在一个同一个程序集中,而闭包流程中并不会使用这个类与其他程序集直接交流。

具体的调用过程

Main中:

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       17 (0x11)
  .maxstack  8
  IL_0000:  call       class [mscorlib]System.Action ExtendMwthod.Program::bibao()
  IL_0005:  callvirt   instance void [mscorlib]System.Action::Invoke()
  IL_000a:  call       valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()
  IL_000f:  pop
  IL_0010:  ret
} // end of method Program::Main

直接调用的依然是bibao方法,再invoke,没发现原因。

.method public hidebysig static class [mscorlib]System.Action 
        bibao() cil managed
{
  // Code size       25 (0x19)
  .maxstack  8
  IL_0000:  newobj     instance void ExtendMwthod.Program/'<>c__DisplayClass1_0'::.ctor()
  IL_0005:  dup
  IL_0006:  ldc.i4.s   100
  IL_0008:  stfld      int32 ExtendMwthod.Program/'<>c__DisplayClass1_0'::i
  IL_000d:  ldftn      instance void ExtendMwthod.Program/'<>c__DisplayClass1_0'::'<bibao>b__0'()
  IL_0013:  newobj     instance void [mscorlib]System.Action::.ctor(object,
                                                                    native int)
  IL_0018:  ret
} // end of method Program::bibao

可以看到自动生成的类在这里实例化了,闭包函数内部已经改写了。起始用字段i和方法'<bibao>b__0'实例化了action,因而在main中调用的时候变量已经包含在action的参数里面带过去了。通过这种方法实现了变量生命周期的延长。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏大内老A

yield在WCF中的错误使用——99%的开发人员都有可能犯的错误[下篇]

昨天写了《yield在WCF中的错误使用——99%的开发人员都有可能犯的错误[上篇]》,引起了一些讨论。关于yield关键字这个语法糖背后的原理(C#编译器将它...

1938
来自专栏Java帮帮-微信公众号-技术文章全总结

Java面试系列7

Java面试系列7 1.HashMap和Hashtable的区别。 HashMap是Hashtable的轻量级实现(非线程安全的实现),二者都实现了Map 接...

3154
来自专栏yl 成长笔记

c# typeof 与 GetType 作用与区别

Used to obtain the "System.Type" object for a type. A 'typeof‘ expression takes ...

3011
来自专栏更流畅、简洁的软件开发方式

静态变量 静态对象 静态函数和非静态函数的区别。(我的理解,大家看看对不对)

先明确一下 语言:asp.net C# 1、静态变量: static string str1 = "1234"; 2、静态对象 static SqlConn...

2305
来自专栏小文博客

写出这个数——《C语言代码笔记》

2582
来自专栏LinkedBear的个人空间

唠唠SE的集合-00——概述 原

                        由于是数组实现,在增和删的时候会牵扯到数组增容、以及拷贝元素,所以慢。

912
来自专栏个人随笔

析构函数(C#)

 析构函数又称终结器,用于析构类的实例。 定义   析构函数(destructor) 与构造函数相反,当对象结束其生命周期时(例如对象所在的函数已调用完毕),系...

3457
来自专栏软件开发 -- 分享 互助 成长

C++ STL 中erase()的使用需要小心

C++ STL极大的方便了用户编写程序,但是同时一不小心也会犯一些错误,如erase()造成迭代器失效经常会引起错误。 错误示例: std::lis...

25310
来自专栏Java技术栈

屌炸天,JDK8的排序大法!!

首先祝大家端午节快乐! 今天总结了下JDK中排序的方法,包括JDK8中强大的lambda表达式及函数式接口运用,不废话,请看下面示例。 public class...

2686
来自专栏Golang语言社区

GO-回调函数

1,什么是回调函数。一个函数中有个参数为函数的指针。该函数自己调用自己指针函数即可; 2,为什么要使用回调函数。抽象画了一个函数,简洁代码。异步执行 3,例子:...

3495

扫码关注云+社区

领取腾讯云代金券