科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道基础软件VisualC# 2.0匿名方法揭密

VisualC# 2.0匿名方法揭密

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

匿名方法是C#2.0的一个新的语言特性。本文的主要内容是提供给读者关于匿名方法的内部实现和工作方式的一个更好的理解。

作者:小刀人编译 来源:VCKBASE 2007年11月11日

关键字:

  • 评论
  • 分享微博
  • 分享邮件
匿名方法的局部变量用法

  到现在为止,我们对匿名方法如何工作以及内部如何实现有了一点基本的理解。从根本上说,C#创建了private方法来包装匿名方法。同时这些方法的签名与它们被分配到的委托相匹配。现在,让我们看看下面的代码:

public class Program
{
 public delegate void MyDelegate();
 public static void Main(string[] args)
 {
  int iTemp = 100;
  MyDelegate dlg = delegate
  {
   Console.WriteLine(iTemp);
  };
  dlg();
 }
}

  对于我们到现在为止对匿名方法已了解的内容来说,这段代码不应该编译。因为我们没有使用如何实例数据成员,C#编译器应该在''Program''类中创建一个private静态方法来包装这个匿名方法。但是新的方法如何访问局部变量呢?这让我们相信该代码将不能被编译。但是令人惊讶的是,C#编译器成功编译了这个代码而没有任何错误或报警。而且,当你执行这个示例时,在控制台屏幕上输出打印出iTemp变量的正确的值。现在让我们进入匿名方法的高级话题。一个匿名方法有封装在其方法体中使用了的环境变量的值的能力。这个封装应用于匿名方法被定义的方法中的所有局部变量。当C#编译器在一个匿名方法的方法体中识别出用到一个局部变量,它就会做如下事情:

  1. 创建一个新的private类作为匿名方法被定义的类的一个内部类。

  2. 在新类(译注:即内部类)中创建一个公共数据成员,使用与用在匿名方法体中的局部变量相同的类型和名称。

  3. 在包装匿名方法的新类中创建一个public实例方法。

  4. 用新类中的声明替代局部变量的声明。创建该新类的一个实例代替局部变量的声明。

  5. 用新类实例的数据成员替代在匿名方法体内部和外部使用的局部变量。

  6. 用在新类中定义的实例方法的地址取代匿名方法的定义。

  因此在编译时,上面的代码将被C#编译器翻译为如下代码:

public class Program
{
 private class InnerClass
 {
  private void InstanceMethod()
  {
   Console.WriteLine(iTemp);
  }
  public int iTemp;
 }
 public delegate void MyDelegate();
 public static void Main(string[] args)
 {
  InnerClass localObject = new InnerClass();
  localObject.iTemp = 100;
  MyDelegate dlg = new MyDelegate(localObject.InstanceMethod);
  dlg();
 }
}

  正如上面的伪代码所示,C#编译器为''Program''类生成了一个private内部类。在匿名方法中使用的局部变量作为新的已创建的内部类的一个实例数据成员而捕获。并且匿名方法本身被包装在内部类的实例方法中。最后,该实例方法在Main方法中作为一个委托处理器而使用。这样,当委托被调用时,对于在被封装入匿名方法中的局部变量将会有一个正确的值。下面图中选定的部分显示了由C#编译器默默添加到''Program'' 类的新的private内部类。


  被用在匿名方法中的局部变量有着超出用到它们的外部常规方法的生命周期。这个技术,在其它语言中,就是大家都知道的closures。除去匿名方法提供的简单语法,closures是匿名方法提供给开发者的一个功能强大的技术。该技术允许委托处理器代码(匿名方法)访问在常规方法内部被定义的局部变量。这就允许out-of-band数据,除了委托参数之外还有数据将被传递到委托,以供在其方法执行时使用。没有这个技术,每个委托和其相应的处理器方法就不得不声明表示局部上下文数据的参数,随着时间的过去这(译注:指不断声明表示局部上下文数据的参数)将变得难于管理。 匿名方法的作用域和局部变量用法

  我们讨论了在方法的主作用域(the main scope)中的匿名方法的实现。当一个匿名方法在一个嵌套作用域中被定义时,并且匿名方法中用到独立作用域级的局部变量,C#为每个作用域创建一个private内部类。比如,假设scope 1有局部变量iTemp,而scope 2,是scope 1的嵌套作用域,有一个局部变量jTemp。让在使用来自scope 1 和 scope 2局部变量iTemp 和 jTemp的 scope 2中,我们定义一个匿名方法。下面的代码显示了上面描述的示例:

public class Program
{
 public delegate void MyDelegate();
 public static void Main(string[] args)
 {
  MyDelegate dlg = null;
  int iTemp = 100;
  if (iTemp > 50)
  {
   int jTemp = 200;
   dlg = delegate
   {
    Console.WriteLine("iTemp: {0}, jTemp: {1}",iTemp,jTemp);
   };
  }
  dlg();
 }
}

  当上面的代码被编译时,C#编译器在''Program''类中创建两个内部类。一个内部类包装局部变量iTemp作为一个public数据成员。第二个内部类包装在嵌套作用域中的局部变量,jTemp,作为一个public数据成员,同时在相同的嵌套作用域中包装匿名方法作为public实例方法。C#编译器为上面的代码生成下面的伪代码:

public class Program
{
 //包装来自外部作用域的局部变量''iTemp''的类
 private class InnerClassScope1
 {
  public int iTemp;
 }
 //包装来自内部作用域和匿名方法的局部变量的类
 private class InnerClassScope2
 {
  public void InstanceMethod()
  {
   Console.WriteLine("iTemp: {0}, jTemp: {1}", localObjectScope1.iTemp, jTemp);
  }
  public InnerClassScope1 localObjectScope1;
  public int jTemp;
 }
 public delegate void MyDelegate();
 public static void Main(string[] args)
 {
  MyDelegate dlg = null;
  InnerClassScope1 localObject1 = new InnerClassScope1();
  localObject1.iTemp = 100;
  if (localObject1.iTemp > 50)
  {
   InnerClassScope2 localObject2 = new InnerClassScope2();
   localObject2.localObjectScope1 = localObject1;
   localObject2.jTemp = 200;
   dlg = new MyDelegate(localObject2.InstanceMethod);
  }
  dlg();
 }
}

  正如上面的代码所示,包装匿名方法的内部类将拥有所有代表外部作用域局部变量的对象,这些变量被用在匿名方法中,像public数据成员。下图显示了C#默默创建的内部类的ILDASM视图:

    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章