匿名方法(Anonymous Methods)

C# 2.0引入了很多语言扩展,最重要的就是泛型(Generics)、匿名方法(Anonymous Methods)、迭代器
(Iterators)和不完全类型(Partial Types)。
【一、什么是匿名方法】
● 匿名方法允许在需要委托值时能够以“内联(in-line)”的方式书写代码块。匿名方法与Lisp语言
中的拉姆达函数(lambda functions)类似。以下为msdn对匿名方法的解释。

在 2.0 之前的 C# 版本中,声明委托的唯一方法是使用命名方法。C# 2.0 引入了匿名方法。

要将代码块传递为委托参数,创建匿名方法则是唯一的方法。例如:

C#
复制代码
// Create a handler for a click event
button1.Click += delegate(System.Object o, System.EventArgs e)
{ System.Windows.Forms.MessageBox.Show("Click!"); };

C#
复制代码
// Create a delegate instance
delegate void Del(int x);

// Instantiate the delegate using an anonymous method
Del d = delegate(int k) { /* ... */ };

如果使用匿名方法,则不必创建单独的方法,因此减少了实例化委托所需的编码系统开销
例如,如果创建方法所需的系统开销是不必要的,在委托的位置指定代码块就非常有用。启动新线程即是一个很好的示例。无需为委托创建更多方法,线程类即可创建一个线程并且包含该线程执行的代码。

C#
复制代码
void StartThread()
{
System.Threading.Thread t1 = new System.Threading.Thread
(delegate()
{
System.Console.Write("Hello, ");
System.Console.WriteLine("World!");
});
t1.Start();
}
匿名方法的参数的范围是 anonymous-method-block

在 目标在块外部 的匿名方法 块内 使用跳转语句(如 goto、break 或 continue)是错误的。在 目标在块内部 的匿名方法块外部 使用跳转语句(如 gotobreakcontinue)也是错误的。

如果局部变量和参数的范围包含匿名方法声明,则该局部变量和参数称为该匿名方法的外部变量或捕获变量。例如,下面代码段中的 n 即是一个外部变量:

C#
复制代码
int n = 0;
Del d = delegate() { System.Console.WriteLine("Copy #:{0}", ++n); };

与局部变量不同,外部变量的生命周期一直持续到引用该匿名方法的委托符合垃圾回收的条件为止。对 n 的引用是在创建该委托时捕获的。
匿名方法不能访问外部范围的 ref 或 out 参数。
anonymous-method-block 中不能访问任何不安全代码。

【匿名方法】
  没有匿名方法之前,我们处理方法和回调方法通常是先显示声明一个专门的委托然后再通过委托调用,而非直接调用方法。所以迄今为止我们还只能将一个实践处理和回调的代码放在一个具体的方法中再为其显式地建立委托。相反,
匿名
方法(anonymous methods)允许将与一个委托关联的代码“内联(in-line)”到使用委托的地方,我们可以很方便地将代码直接写在委托实例中。除了看起来舒服,匿名方法还共享对本地语句所包含的函数成员的访问。如果想在命名方法(区别于匿名方法)中达成这种共享,需要手动创建一个辅助类并将本地成员“提升(lifting)”到这个类的域中。(其实就是将委托的代码直接置换到使用委托的地方,这样就不用再实例化委托,从而节省编码系统开销)
下面的例子展示了从一个包含一个列表框、一个文本框和一个按钮的窗体中获取一个简单的输入。
当按钮按下时文本框中的文本会被添加到列表框中。
class InputForm: Form
{
ListBox listBox;
TextBox textBox;
Button addButton;
public MyForm() {
listBox = new ListBox(...);
textBox = new TextBox(...);
addButton = new Button(...);
addButton.Click += new EventHandler(AddClick);
}
void AddClick(object sender, EventArgs e) {
listBox.Items.Add(textBox.Text);
}
}
尽管对按钮的Click事件的响应只有一条语句,这条语句也必须放到一个独立的具有完整的参数列表的方法中,并且要手动创建引用该方法的EventHandler委托。使用匿名方法,事件处理的代码会变得更加简洁:
class InputForm: Form
{
ListBox listBox;
TextBox textBox;
Button addButton;
public MyForm() {
listBox = new ListBox(...);
textBox = new TextBox(...);
addButton = new Button(...);
addButton.Click += delegate {
listBox.Items.Add(textBox.Text);
};
}
}
一个匿名方法由关键字delegate一个可选的参数列表组成,并将语句放入“{”和“}”限定符中。前面例子中的匿名方法没有使用提供给委托的参数,因此可以省略参数列表。要想访问参数,你名方法应该包含一个参数列表:
addButton.Click += delegate(object sender, EventArgs e) {
MessageBox.Show(((Button)sender).Text);
};
上面的例子中,在匿名方法和EventHandler委托类型(Click事件的类型)之间发生了一个隐式的转换。这个隐式的转换是可行的,因为这个委托的参数列表和返回值类型和匿名方法是兼容的。精确的兼容规则如下:
? 当下面条例中有一条为真时,则委托的参数列表和匿名方法是兼容的:
o 匿名方法没有参数列表且委托没有输出(out)参数。(匿名方法无参,且委托为out)
o 匿名方法的参数列表在参数数目、类型和修饰符上与委托参数精确匹配。(匿名方法与委托在参数数目,类型,修饰符完全一样)
? 当下面的条例中有一条为真时,委托的返回值与匿名方法兼容:
o 委托的返回值类型是void且匿名方法没有return语句或其return语句不带任何表达式。
o 委托的返回值类型不是void但和匿名方法的return语句关联的表达式的值可以被显式地转换为委
托的返回值类型。
只有参数列表和返回值类型都兼容的时候,才会发生匿名类型向委托类型的隐式转换。
下面的例子使用了匿名方法对函数进行了“内联(in-lian)”。匿名方法被作为一个Function委托类型传递。
using System;
delegate double Function(double x);
class Test
{
static double[] Apply(double[] a, Function f) {
double[] result = new double[a.Length];
for (int i = 0; i < a.Length; i++) result[i] = f(a[i]);
return result;
}
static double[] MultiplyAllBy(double[] a, double factor) {
return Apply(a, delegate(double x) { return x * factor; });
}
static void Main() {
double[] a = {0.0, 0.5, 1.0};
double[] squares = Apply(a, delegate(double x) { return x * x; });
double[] doubles = MultiplyAllBy(a, 2.0);
}
}
Apply方法需要一个给定的接受double[]元素并返回double[]作为结果的Function。在Main方法中,传递给Apply方法的第二个参数是一个匿名方法,它与Function委托类型是兼容的。这个匿名方法只简单地返回每个元素的平方值,因此调用Apply方法得到的double[]包含了a中每个值的平方值。
MultiplyAllBy方法通过将参数数组中的每一个值乘以一个给定的factor来建立一个double[]并返回。为了产生这个结果,MultiplyAllBy方法调用了Apply方法,向它传递了一个能够将参数x与factor相乘的匿名方法。
如果一个本地变量或参数的作用域包括了匿名方法,则该变量或参数称为匿名方法的外部变量(outer variables)。在MultiplyAllBy方法中,a和factor就是传递给Apply方法的匿名方法的外部变量。通常,一个局部变量的生存期被限制在块内或与之相关联的语句内。然而,一个被捕获的外部变量的生存期要扩展到至少对匿名方法的委托引用符合垃圾收集条件时。

【方法组转换】
像前面章节中描述过的那样,一个匿名方法可以被隐式转换为一个兼容的委托类型。C# 2.0允许对一组方法进行相同的转换,即任何时候都可以省略一个委托的显式实例化。例如,下面的语句:
addButton.Click += new EventHandler(AddClick);
Apply(a, new Function(Math.Sin));
还可以写做:
addButton.Click += AddClick;
Apply(a, Math.Sin);
当使用短形式时,编译器可以自动地推断应该实例化哪一个委托类型,不过除此之外的效果都和长形式相同。

你可能感兴趣的:(匿名方法(Anonymous Methods))