新书推介:《语义网技术体系》
作者:瞿裕忠,胡伟,程龚
   XML论坛     W3CHINA.ORG讨论区     >>计算机科学论坛<<     SOAChina论坛     Blog     开放翻译计划     新浪微博  
 
  • 首页
  • 登录
  • 注册
  • 软件下载
  • 资料下载
  • 核心成员
  • 帮助
  •   Add to Google

    >> 本版讨论.NET,C#,ASP,VB技术
    [返回] 计算机科学论坛计算机技术与应用『 Dot NET,C#,ASP,VB 』 → CLR 中匿名函数的实现原理浅析(草稿) 查看新帖用户列表

      发表一个新主题  发表一个新投票  回复主题  (订阅本版) 您是本帖的第 4070 个阅读者浏览上一篇主题  刷新本主题   树形显示贴子 浏览下一篇主题
     * 贴子主题: CLR 中匿名函数的实现原理浅析(草稿) 举报  打印  推荐  IE收藏夹 
       本主题类别:     
     admin 帅哥哟,离线,有人找我吗?
      
      
      
      威望:9
      头衔:W3China站长
      等级:计算机硕士学位(管理员)
      文章:5255
      积分:18407
      门派:W3CHINA.ORG
      注册:2003/10/5

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给admin发送一个短消息 把admin加入好友 查看admin的个人资料 搜索admin在『 Dot NET,C#,ASP,VB 』的所有贴子 点击这里发送电邮给admin  访问admin的主页 引用回复这个贴子 回复这个贴子 查看admin的博客楼主
    发贴心情 CLR 中匿名函数的实现原理浅析(草稿)


    发信人: flier (小海 [寻找风车中]), 信区: DotNET
    标  题: CLR 中匿名函数的实现原理浅析(草稿)
    发信站: BBS 水木清华站 (Mon Mar 15 22:07:22 2004), 转信

    开两会偶的blog也歇菜了,先发个草稿,等过两天有空再整理吧

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

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

    ...
    button1.Click += new EventHandler(button1_Click);
    ...
    void button1_Click(Object sender, EventArgs e) {
       // Do something, the button was clicked...
    }
    ...
    [/quote]
        可以被简化为直接使用匿名函数构造,如
    [quote]
    ...
    button1.Click += delegate(Object sender, EventArgs e) {
      // Do something, the button was clicked...
    }
    ...
    [/quote]
        关于匿名函数的使用方法可以参考Jeffrey Richter的[url=http://www.codeguru.com/Csharp/Csharp/cs_delegates/print.php/c4767/]Workingwith Delegates Made Easier with C# 2.0[/url]一文。简要说来就是C#编译器自动将匿名函数代码转移到一个自动命名函数中,将原来需要用户手工完成的工作自动完成。例如构造一个私有静态函数,如
    [quote]
    class AClass {
      static void CallbackWithoutNewingADelegateObject() {
        ThreadPool.QueueUserWorkItem(delegate(Object obj) { Console.WriteLine(obj); }, 5);
      }
    }
    [/quote]
        被编译器自动转换为
    [quote]
    class AClass {
      static void CallbackWithoutNewingADelegateObject() {
        ThreadPool.QueueUserWorkItem(new WaitCallback(__AnonymousMethod$00000002), 5);
      }

      private static void __AnonymousMethod$00000002(Object obj) {
        Console.WriteLine(obj);
      }
    }
    [/quote]
        而这里自动生成的函数是否为static,编译器根据使用此函数的地方是否static决定。这也是为什么C# 2.0规范里面禁止使用goto, break和continue语句从一个匿名方法里跳出,或从外面跳入其中的原因,因为他们代码虽然写在一个作用域里面,但实际上实现上并不在一起。

        更方便的是编译器可以根据匿名函数使用的情况,自动判断函数参数,无需用户在定义时指定,如
    [quote]
    button1.Click += delegate(Object sender, EventArgs e) { MessageBox.Show(&quot;The Button was clicked!&quot;); };
    [/quote]
        在不使用参数时,完全等价于
    [quote]

    button1.Click += delegate { MessageBox.Show(&quot;The Button was clicked!&quot;); };
    [/quote]

        相对于匿名函数的实现来说,比较复杂的是匿名函数对于其父作用域中变量的使用及其实现。MS的[url=http://weblogs.asp.net/grantri/]GrantRi[/url]在其blog上有一系列的讨论文章。
        [url=http://weblogs.asp.net/grantri/archive/2004/02/05/68526.aspx]AnonymousMethods, Part 1 of ?[/url]
        [url=http://weblogs.asp.net/grantri/archive/2004/03/08/86291.aspx]AnonymousMethods, Part 2 of ?[/url]
        [url=http://weblogs.asp.net/grantri/archive/2004/03/09/87002.aspx]AnonymousMethod Part 2 answers [/url]

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

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

    public void Method1()
    {
      int i=0;

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

      d1();
    }
    [/quote]
        自动生成的包装代码类似如下
    [quote]
    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();
    }
    [/quote]
        但对于有多个局部变量作用域的情况就比较复杂了,例如Grant Ri在其例子中给出的代码
    [quote]
    delegate void NoArgs();

    void SomeMethod()
    {
        NoArgs [] methods = new NoArgs[10];
        int outer = 0;
        for (int i = 0; i &lt; 10; i++)
        {
            int inner = i;
            methods[i] = delegate {
                Console.WriteLine(&quot;outer = {0}&quot;, outer++);
                Console.WriteLine(&quot;i = {0}&quot;, i);
                Console.WriteLine(&quot;inner = {0}&quot;, ++inner);
            };
            methods[i]();
        }
        for (int j = 0; j &lt; methods.Length; j++)
            methods[j]();
    }
    [/quote]
        就需要一个类封装变量outer;一个类封装变量i;另外一个类封装inner和匿名函数,并引用前面两个封装类的实例。因为变量outer、i和inner有着不同的作用域,呵呵。伪代码如下:
    [quote]
    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(&quot;outer = {0}&quot;, this.$locals$00000009.outer++);
        Console.WriteLine(&quot;i = {0}&quot;, this.$locals$0000000b.i);
        Console.WriteLine(&quot;inner = {0}&quot;, ++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 &lt; 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]();

        local2.i++;
        local1.outer++;
      }

      for (int j = 0; j &lt; methods.Length; j++)
        methods[j]();
    }
    [/quote]

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

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

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

      Say;
    end;
    [/quote]
        系统生成函数Say代码时,将使用到的上级变量如Name放入到一个自动生成的类型($Unnamed1)中,然后作为函数参数传递给Say函数,伪代码类似
    [quote]
    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;



    --
    .    生命的意义在于   /\   ____\ /\_ \   /\_\    http://flier_lu.blogone.net.                            .  
    .        希望         \ \  \___/_\/\ \   \/_/__     __    _ _★              .  
    .        工作          \ \   ____\\ \ \    /\  \  /'__`\ /\`'_\              .  
    .      爱你的人         \ \  \___/ \ \ \___\ \  \/\ __// \ \ \/              .  
    .     和你爱的人         \ \___\    \ \_____\ \__\ \____\ \ \_\              .  
    .        ……             \/___/     \/_____/\/__/\/____/  \/_/ @nsfocus.com.  


    ※ 来源:·BBS 水木清华站 smth.org·[FROM: 211.167.254.*]
    上一篇
    返回上一页
    回到目录
    回到页首
    下一篇


       收藏   分享  
    顶(0)
      




    ----------------------------------------------

    -----------------------------------------------

    第十二章第一节《用ROR创建面向资源的服务》
    第十二章第二节《用Restlet创建面向资源的服务》
    第三章《REST式服务有什么不同》
    InfoQ SOA首席编辑胡键评《RESTful Web Services中文版》
    [InfoQ文章]解答有关REST的十点疑惑

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2004/11/9 2:26:00
     
     GoogleAdSense
      
      
      等级:大一新生
      文章:1
      积分:50
      门派:无门无派
      院校:未填写
      注册:2007-01-01
    给Google AdSense发送一个短消息 把Google AdSense加入好友 查看Google AdSense的个人资料 搜索Google AdSense在『 Dot NET,C#,ASP,VB 』的所有贴子 点击这里发送电邮给Google AdSense  访问Google AdSense的主页 引用回复这个贴子 回复这个贴子 查看Google AdSense的博客广告
    2025/7/23 0:46:00

    本主题贴数1,分页: [1]

    管理选项修改tag | 锁定 | 解锁 | 提升 | 删除 | 移动 | 固顶 | 总固顶 | 奖励 | 惩罚 | 发布公告
    W3C Contributing Supporter! W 3 C h i n a ( since 2003 ) 旗 下 站 点
    苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
    93.994ms