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

http://tech.ddvip.com   2007年10月05日    社区交流

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

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

以下为引用:
delegatevoidDelegate1();
  privatesealedclass__LocalsDisplayClass$00000002
{
 publicinti;
  publicvoid__AnonymousMethod$00000001()
 {
  this.i++;
 }
};
  publicvoidMethod1()
{
 __LocalsDisplayClass$00000002local1=new__LocalsDisplayClass$00000002();
 local1.i=0;
  Delegate1d1=newDelegate1(local1.__AnonymousMethod$00000001);
  d1();
}

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

以下为引用:
delegatevoidNoArgs();
  voidSomeMethod()
{
  NoArgs[]methods=newNoArgs[10];
  intouter=0;
  for(inti=0;i<10;i++)
  {
    intinner=i;
    methods[i]=delegate{
      Console.WriteLine("outer={0}",outer++);
      Console.WriteLine("i={0}",i);
      Console.WriteLine("inner={0}",++inner);
    };
    methods[i]();
  }
  for(intj=0;j<methods.Length;j++)
    methods[j]();
}

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

以下为引用:
privatesealedclass__LocalsDisplayClass$00000008
{
 publicintouter;
  };
privatesealedclass__LocalsDisplayClass$0000000a
{
 publicinti;
  };
privatesealedclass__LocalsDisplayClass$0000000c
{
 publicintinner;
  public__LocalsDisplayClass$00000008$locals$00000009;
 public__LocalsDisplayClass$0000000a$locals$0000000b;
  publicvoid__AnonymousMethod$00000007()
 {
  Console.WriteLine("outer={0}",this.$locals$00000009.outer++);
  Console.WriteLine("i={0}",this.$locals$0000000b.i);
  Console.WriteLine("inner={0}",++this.inner);
 }
};
  publicvoidSomeMethod()
{
 NoArgs[]methods=newNoArgs[10];
  __LocalsDisplayClass$00000008local1=new__LocalsDisplayClass$00000008();
 local1.outer=0;
  __LocalsDisplayClass$0000000alocal2=new__LocalsDisplayClass$0000000a();
 local2.i=0;
  while(local2.i<10)
 {
  __LocalsDisplayClass$0000000clocal3=new__LocalsDisplayClass$0000000c();
  local3.$locals$00000009=local1;
  local3.$locals$0000000b=local2;
  local3.inner=local1.i;
  methods[local2.i]=newNoArgs(local3.__AnonymousMethod$00000007);
  methods[local2.i]();
 }
  for(intj=0;j<methods.Length;j++)
  methods[j]();
}

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

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

以下为引用:
procedureSayHello;
var
 Name:string;
  procedureSay;
 begin
  WriteLn(Name);
 end;
begin
 Name:='FlierLu';
  Say;
end;

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

以下为引用:
type
 $Unnamed1=record
  Name:string;
 end;
  procedure@1$SayHello$Say(varUnnamedParam:$Unnamed1);
begin
 WriteLn(UnnamedParam.Name);
end;
  procedureSayHello;
var
 Name:string;
 Unnamed1:$Unnamed1;
begin
 Name:='FlierLu';
  Unnamed1.Name:=Name;
  Say(Unnamed1);
end;

责编:豆豆技术应用

正在加载评论...