CLR 中匿名函数的实现原理浅析

原创 2004年04月07日 14:37:00

http://flier_lu.blogone.net/?id=1397624

CLR 中匿名函数的实现原理浅析

    C# 2.0中提供了通过delegate实现匿名函数功能,能有效地减少用户的薄记代码工作,例如

以下为引用:

...
button1.Click += new EventHandler(button1_Click);
...
void button1_Click(Object sender, EventArgs e) {
   // Do something, the button was clicked...
}
...



    可以被简化为直接使用匿名函数构造,如

以下为引用:

...
button1.Click += delegate(Object sender, EventArgs e) {
  // Do something, the button was clicked...
}
...



    关于匿名函数的使用方法可以参考Jeffrey Richter的Working with Delegates Made Easier with C# 2.0一文。简要说来就是C#编译器自动将匿名函数代码转移到一个自动命名函数中,将原来需要用户手工完成的工作自动完成。例如构造一个私有静态函数,如

以下为引用:

class AClass {
  static void CallbackWithoutNewingADelegateObject() {
    ThreadPool.QueueUserWorkItem(delegate(Object obj) { Console.WriteLine(obj); }, 5);
  }
}



    被编译器自动转换为

以下为引用:

class AClass {
  static void CallbackWithoutNewingADelegateObject() {
    ThreadPool.QueueUserWorkItem(new WaitCallback(__AnonymousMethod$00000002), 5);
  }

  private static void __AnonymousMethod$00000002(Object obj) {
    Console.WriteLine(obj);
  }
}



    而这里自动生成的函数是否为static,编译器根据使用此函数的地方是否static决定。这也是为什么C# 2.0规范里面禁止使用goto, break和continue语句从一个匿名方法里跳出,或从外面跳入其中的原因,因为他们代码虽然写在一个作用域里面,但实际上实现上并不在一起。
    更方便的是编译器可以根据匿名函数使用的情况,自动判断函数参数,无需用户在定义时指定,如

以下为引用:

button1.Click += delegate(Object sender, EventArgs e) { MessageBox.Show("The Button was clicked!"); };



    在不使用参数时,完全等价于

以下为引用:

button1.Click += delegate { MessageBox.Show("The Button was clicked!"); };




    相对于匿名函数的实现来说,比较复杂的是匿名函数对于其父作用域中变量的使用及其实现。MS的Grant Ri在其blog上有一系列的讨论文章。
    Anonymous Methods, Part 1 of ?
    Anonymous Methods, Part 2 of ?
    Anonymous Method Part 2 answers 

    需要解决的问题有两个:一是不在一个变量作用域中的匿名函数如何访问父函数和类的变量;二是匿名函数使用到的变量的生命周期必须与其绑定,而不能与父函数的调用生命周期绑定。这两个问题使得C#编译器选择较为复杂的独立类封装方式实现匿名函数和相关变量生命周期的管理。

    首先,匿名函数使用到的父函数中局部变量,无聊是引用类型还是值类型,都必须从栈变量转换为堆变量,以便在其作用域外的匿名函数实现代码可以访问并控制生命周期。因为栈变量的生命周期与其所有者函数是一致的,所有者函数退出后,其堆栈自动恢复到调用函数前,也就无法完成变量生命周期与函数调用生命周期的解耦。
    例如下面这个简单的匿名函数中,使用了父函数的局部变量,虽然此匿名函数只在父函数里面使用,但C#编译器还是使用独立类对其使用到的变量进行了包装。

以下为引用:

delegate void Delegate1();

public void Method1()
{
  int i=0;

  Delegate1 d1 = delegate() { i++; };

  d1();
}



    自动生成的包装代码类似如下

以下为引用:

delegate void Delegate1();

private sealed class __LocalsDisplayClass$00000002
{
  public int i;

  public void __AnonymousMethod$00000001()
  {
    this.i++;
  }
};

public void Method1()
{
  __LocalsDisplayClass$00000002 local1 = new __LocalsDisplayClass$00000002();
  local1.i = 0;

  Delegate1 d1 = new Delegate1(local1.__AnonymousMethod$00000001);

  d1();
}



    但对于有多个局部变量作用域的情况就比较复杂了,例如Grant Ri在其例子中给出的代码

以下为引用:

delegate void NoArgs();

void SomeMethod()
{
    NoArgs [] methods = new NoArgs[10];
    int outer = 0;
    for (int i = 0; i < 10; i++)
    {
        int inner = i;
        methods[i] = delegate {
            Console.WriteLine("outer = {0}", outer++);
            Console.WriteLine("i = {0}", i);
            Console.WriteLine("inner = {0}", ++inner);
        };
        methods[i]();
    }
    for (int j = 0; j < methods.Length; j++)
        methods[j]();
}



    就需要一个类封装变量outer;一个类封装变量i;另外一个类封装inner和匿名函数,并引用前面两个封装类的实例。因为变量outer、i和inner有着不同的作用域,呵呵。伪代码如下:

以下为引用:

private sealed class __LocalsDisplayClass$00000008
{
  public int outer;

};
private sealed class __LocalsDisplayClass$0000000a
{
  public int i;

};
private sealed class __LocalsDisplayClass$0000000c
{
  public int inner;

  public __LocalsDisplayClass$00000008 $locals$00000009;
  public __LocalsDisplayClass$0000000a $locals$0000000b;

  public void __AnonymousMethod$00000007()
  {
    Console.WriteLine("outer = {0}", this.$locals$00000009.outer++);
    Console.WriteLine("i = {0}", this.$locals$0000000b.i);
    Console.WriteLine("inner = {0}", ++this.inner);
  }
};

public void SomeMethod()
{
  NoArgs [] methods = new NoArgs[10];

  __LocalsDisplayClass$00000008 local1 = new __LocalsDisplayClass$00000008();
  local1.outer = 0;

  __LocalsDisplayClass$0000000a local2 = new __LocalsDisplayClass$0000000a();
  local2.i = 0;

  while(local2.i < 10)
  {
    __LocalsDisplayClass$0000000c local3 = new __LocalsDisplayClass$0000000c();
    local3.$locals$00000009 = local1;
    local3.$locals$0000000b = local2;
    local3.inner = local1.i;

    methods[local2.i] = new NoArgs(local3.__AnonymousMethod$00000007);
    methods[local2.i]();
  }

  for (int j = 0; j < methods.Length; j++)
    methods[j]();
}




    总结其规律就是每个不同的局部变量作用域会有一个单独的类进行封装,子作用域中如果使用到父作用域的局部变量,则子作用域的封装类引用父作用域的封装类。相同作用域的变量和匿名方法由封装类绑定到一起,维护其一致的生命周期。

    相对于MS较为复杂的实现,Delphi.NET对嵌套函数则使用较为简单的参数传递方式,因为嵌套函数没有那么复杂的变量生命期管理要求,如

以下为引用:

procedure SayHello;
var
  Name: string;

  procedure Say;
  begin
    WriteLn(Name);
  end;
begin
  Name := 'Flier Lu';

  Say;
end;



    系统生成函数Say代码时,将使用到的上级变量如Name放入到一个自动生成的类型($Unnamed1)中,然后作为函数参数传递给Say函数,伪代码类似

以下为引用:

type
  $Unnamed1 = record
    Name: string;
  end;

procedure @1$SayHello$Say(var UnnamedParam: $Unnamed1);
begin
  WriteLn(UnnamedParam.Name);
end;

procedure SayHello;
var
  Name: string;
  Unnamed1: $Unnamed1;
begin
  Name := 'Flier Lu';

  Unnamed1.Name := Name;

  Say(Unnamed1);
end;


 

CLR 中代码访问安全检测实现原理

注:引用http://www.sudu.cn/info/html/edu/network_technology/20041027/174726.html 在传统的操作系统级安全模型中,安全管理的粒度都...
  • keduosou
  • keduosou
  • 2008年11月21日 09:28
  • 876

Facebook架构简述

随着一些技术交流,逐渐能看到 Facebook 技术人员分享的经验。近期这个 http://www.geeksessions.com/ 站点上看到 Facebook 的 Lucas Nealan 分...
  • jiarch
  • jiarch
  • 2010年07月24日 21:42
  • 188

Facebook存储解析:管理最复杂的系统

Facebook存储解析:管理最复杂的系统 存储在线  09年02月01日 20:18 【原创】 作者:Rob 责任编辑:Admin 导读:Facebook就像是一个传奇,很难相信,前哈佛大学二年级...
  • tglg
  • tglg
  • 2009年02月09日 17:28
  • 437

Facebook架构解读

从我看过的各种资料,还有与各式人等的交谈中,可以得出Facebook现在的架构是这样的: Web前端用PHP语言编写,然后用HipHop Compiler[1]转换为C++语言,再用g++编译器...
  • haydenwang8287
  • haydenwang8287
  • 2016年01月18日 17:28
  • 219

浅谈Javascript的匿名函数中的this对象

在一般情况下,this对象时在运行时基于函数的执行环境绑定的:在全局函数中,this...
  • u013250416
  • u013250416
  • 2014年11月06日 21:59
  • 2366

IronPython for ASP.NET 的原理分析(一):如何在 CLR 类型上实现动态性

如何在 CLR 类型上实现 IronPython 语言的动态特性,简单的讲,可以通过 ICustomAttributes 接口来实现。ICustomAttributes 接口是在 IronPyth...
  • inelm
  • inelm
  • 2006年12月10日 16:18
  • 333

Python匿名函数详解

文章导读: 以前自己一直没搞明白Python中的匿名函数,现在拿这个问题基本上搞明白了,拿自己的理解整成一篇文章,附带大量例子,让其更加好理解。 ------- 在编程语言中,函数的应用: 1. 代...
  • Jerry_1126
  • Jerry_1126
  • 2014年10月15日 12:54
  • 4440

JavaScript关于闭包,匿名函数,this,对象等的一些理解

闭包原本就是指所有的函数,但我们一般是指能够读取其他函数内部变量的函数,主要有两个作用:一个是可以读取函数内部的变量,另一个就是让这些变量的值始终保持在内存中。当函数a的内部函数b被函数a外的一个变量...
  • jinhailang132
  • jinhailang132
  • 2014年03月18日 22:46
  • 804

Python中lambda匿名函数

参考:http://www.cnblogs.com/coderzh/archive/2010/04/30/python-cookbook-lambda.html lambda函数也叫匿名函数,即...
  • tanzhangwen
  • tanzhangwen
  • 2014年10月25日 12:43
  • 1362

jQuery源码解析之自调用匿名函数

(function( window, undefined ) { // 构造jQuery 对象 var jQuery = (function() { var jQuery = function(...
  • ElephantBoy
  • ElephantBoy
  • 2016年10月17日 17:09
  • 1186
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:CLR 中匿名函数的实现原理浅析
举报原因:
原因补充:

(最多只允许输入30个字)