匿名方法 - 幕后花絮






4.48/5 (13投票s)
在本文中,我们将看到在使用匿名方法时幕后发生的事情
引言
我认为软件开发世界中最神秘的事情之一是编译器。让我们专门谈谈 C# 编译器。
由于历史原因,C# 编译器是用 C++ 编写的。C# 编译器获取源文件,然后生成 IL 代码。但我们不知道 C# 编译器在生成 IL 代码时内部发生了什么。在此过程中,C# 编译器还会创建各种新的结构。
匿名方法 - 幕后花絮
匿名方法允许我们定义未命名的(匿名)方法,匿名方法最常见的用法是委托。
例如,
btnLambda.Click += (_sender, _args) =>
{
MessageBox.Show("This is an anonymous function");
};
简单来说,这个语句将一个事件处理程序添加到按钮的点击事件中。但是幕后发生了什么?编译器会进行额外的转换吗?
首先,我们用反编译器打开这个应用程序。

我们看到编译器生成了一个不同的方法和委托,名为 <WindowLoaded>b__0
和 CS$<>9__CachedAnonymousMethodDelegate1
。此方法包含匿名方法的内联语句。

如果我们将这个事件处理程序的赋值放入 WPF Window 的 Loaded
事件中,我们可以看到生成的 IL 代码如下

简单来说,在这个方法中,首先,编译器创建一个委托,该委托将生成的 static
方法作为参数,然后将该委托添加为按钮的点击事件处理程序。
在匿名方法中使用局部变量
我认为在匿名方法中使用局部变量是匿名方法最重要的特性。如果我们将委托显式地添加到事件中作为事件处理程序,我们不能在这个方法中使用任何局部变量,而这个方法是在事件处理程序的赋值操作中进行的。
让我们演示这个场景。
private void Window_Loaded(object sender, RoutedEventArgs e)
{
int x = 10;
double y = 198.30;
btnLambda.Click += (_sender, _args) =>
{
MessageBox.Show(String.Format("This is an anonymous function
result={0}",(x*y)));
};
}
在这个方法中,匿名方法使用在事件处理程序赋值所在的方法中定义的局部变量。
现在让我们再次用反编译器查看该项目的新类视图。

在这种情况下,编译器生成了一个名为 <>c__DisplayClass1
的新类型。此类型包含 2 个我们在匿名方法声明中使用的字段,并且还包含一个方法声明,该方法执行我们在匿名方法声明中指定的动作,正如我们在上一节中讨论的那样。

IL 代码中的 Window_Loaded
方法

在这个方法中,首先,编译器生成 new <>c__DisplayClass1
对象,然后赋值字段值。最后,编译器通过给出位于 <>c__DisplayClass1
类型中的方法创建一个委托,并将该委托添加到按钮的 Click
事件中作为事件处理程序。如果我们在匿名方法中使用局部对象引用,则情况将相同。编译器生成的类型将包含相关字段,以便访问使用的对象。
结论
正如您所看到的,编译器确实很神秘。当编程语言提供一些新特性时,编译器会处理一些额外的转换,并使开发人员的生活更轻松。
也许这就是我喜欢编译器和编程语言的原因。 :)
希望这有帮助!
历史
- 2011 年 2 月 9 日:初始帖子