C# 2.0 - Anonymous Methods
[ 2005-10-20 15:55:25 | 作者: yuhen ]
本文一些资料来源于李建忠先生的讲演稿。
1. 什么是匿名方法?
匿名方法允许我们以一种“内联”的方式来编写方法代码,将代码直接与委托实例相关联,从而使得委托实例化的工作更加直观和方便。
我们先看一段代码。
上面的代码是一个典型的事件(委托)方法,虽然Click事件代码非常简单,但是我们还是必须写一个这样的方法,并创建一个委托对象。在2.0中,提供了一种称之为匿名方法的措施来改进类似的代码。
是不是更简单?我们反编译一下会发现,编译器依然创建了一个委托字段和方法,说白了就是编译器替我们完成了原本需要我们输入的代码。呵呵,偷懒无罪,偷懒有理!
那么匿名方法可以在那些地方使用呢?继续往下看。
我们可以使用匿名方法改写为:
2. 参数列表
匿名方法可以在delegate关键字后面跟上委托签名的参数列表(可以不指定),后面的代码块则可以访问这些参数。
注意:
(1) 参数要么省略,要么必须全部添加,和委托签名必须保持一致。
(2) 不指定方法列表和参数列表为空是不同的。
3. 返回值
如果委托类型的返回值为void,匿名方法里便不能返回任何类型;
如果委托类型的返回值不是void,匿名方法里的返回值必须和委托类型相一致;
4. 外部变量
一些局部变量和参数有可能被匿名方法所使用,他们被称为“匿名方法的外部变量”。
外部变量的生存期会由于“匿名方法的捕获效益”而延长——直到委托实例不再被引用为止。
上面的代码中,Test方法的参数"i"被匿名方法所使用,因此i就是匿名方法的外部变量。
猜测一下最后输出的结果是多少?"1, 1"?还是"1, 0"?那么你错了,正确的结果应该是"0",为什么?因为我们仅仅创建了一个匿名方法,并没有去调用这个委托。:-) 好了,在Console.WriteLine(i) 前面加一个 "t(i);" 这回就对了。
注意,"i"的值在被匿名方法调用后发生了变更,可我们并没有在"t(i);"语句中使用 ref 或者 out 关键字,这和你熟悉的拷贝传递有所不同是吗?看看反编译的代码你就明白了。
看了反编译的结果,你会发现使用外部变量的成本很高,而且会带来潜在的危险,因此不推荐大规模使用。
5. 委托类型的推断
C# 2.0 允许我们在进行委托实例化时,省略掉委托类型,而直接采用方法名,C#编译器会做合理的推断。
看看反编译后的Main方法中 2.0 的代码样子。
呵呵,还是偷懒无敌啊。
评论Feed: http://www.rainsts.net/feed.asp?q=comment&id=111
1. 什么是匿名方法?
匿名方法允许我们以一种“内联”的方式来编写方法代码,将代码直接与委托实例相关联,从而使得委托实例化的工作更加直观和方便。
我们先看一段代码。
private void button1_Click(object sender, EventArgs e)
{
MessageBox.Show("OK!");
}
private void Form1_Load(object sender, EventArgs e)
{
this.button1.Click += new EventHandler(this.button1_Click);
}上面的代码是一个典型的事件(委托)方法,虽然Click事件代码非常简单,但是我们还是必须写一个这样的方法,并创建一个委托对象。在2.0中,提供了一种称之为匿名方法的措施来改进类似的代码。
private void Form1_Load(object sender, EventArgs e)
{
this.button1.Click += delegate
{
MessageBox.Show("OK!");
}
}是不是更简单?我们反编译一下会发现,编译器依然创建了一个委托字段和方法,说白了就是编译器替我们完成了原本需要我们输入的代码。呵呵,偷懒无罪,偷懒有理!
[CompilerGenerated]
private static EventHandler <>9__CachedAnonymousMethodDelegate1;
private void Form1_Load(object sender, EventArgs e)
{
if (Form1.<>9__CachedAnonymousMethodDelegate1 == null)
{
Form1.<>9__CachedAnonymousMethodDelegate1 = new EventHandler(Form1.<Form1_Load>b__0);
}
this.button1.Click += Form1.<>9__CachedAnonymousMethodDelegate1;
}
[CompilerGenerated]
private static void <Form1_Load>b__0(object, EventArgs)
{
MessageBox.Show("OK!");
}
那么匿名方法可以在那些地方使用呢?继续往下看。
public void Start()
{
Thread t = new Thread(new ThreadStart(ThreadProc));
t.Start();
}
private void ThreadProc()
{
Console.WriteLine("Hello, World!");
}我们可以使用匿名方法改写为:
public void Start()
{
Thread t = new Thread(delegate() // 由于Thread有多个构造方法,因此指定参数列表。
{
Console.WriteLine("Hello, World!");
});
t.Start();
}2. 参数列表
匿名方法可以在delegate关键字后面跟上委托签名的参数列表(可以不指定),后面的代码块则可以访问这些参数。
private void Form1_Load(object sender, EventArgs e)
{
this.button1.Click += delegate(object sender, EventArgs e)
{
MessageBox.Show((Button)sender.Text + " Clicked!");
}
}注意:
(1) 参数要么省略,要么必须全部添加,和委托签名必须保持一致。
(2) 不指定方法列表和参数列表为空是不同的。
delegate {...} // 不指定参数列表
delegate() { ... } // 参数列表为空3. 返回值
如果委托类型的返回值为void,匿名方法里便不能返回任何类型;
如果委托类型的返回值不是void,匿名方法里的返回值必须和委托类型相一致;
public delegate int TestHandler(int x);
static void Test()
{
TestHandler t = delegate(int x)
{
return x * 2;
};
int i = t(2);
Console.WriteLine(i);
}4. 外部变量
一些局部变量和参数有可能被匿名方法所使用,他们被称为“匿名方法的外部变量”。
外部变量的生存期会由于“匿名方法的捕获效益”而延长——直到委托实例不再被引用为止。
public class Program
{
public delegate int TestHandler(int x);
static void Test(int i)
{
TestHandler t = delegate
{
Console.WriteLine(++i);
return 0;
};
Console.WriteLine(i);
}
static void Main(string[] args)
{
Test(0);
}
}上面的代码中,Test方法的参数"i"被匿名方法所使用,因此i就是匿名方法的外部变量。
猜测一下最后输出的结果是多少?"1, 1"?还是"1, 0"?那么你错了,正确的结果应该是"0",为什么?因为我们仅仅创建了一个匿名方法,并没有去调用这个委托。:-) 好了,在Console.WriteLine(i) 前面加一个 "t(i);" 这回就对了。
public class Program
{
public delegate int TestHandler(int x);
static void Test(int i)
{
TestHandler t = delegate
{
Console.WriteLine(++i);
return 0;
};
t(i); // <---------- Add Here!!!
Console.WriteLine(i);
}
static void Main(string[] args)
{
Test(0);
}
}注意,"i"的值在被匿名方法调用后发生了变更,可我们并没有在"t(i);"语句中使用 ref 或者 out 关键字,这和你熟悉的拷贝传递有所不同是吗?看看反编译的代码你就明白了。
public class Program
{
public int i;
[CompilerGenerated]
private sealed class <>c__DisplayClass1
{
public int <Test>b__0(int)
{
int num2;
this.i = num2 = this.i + 1;
Console.WriteLine(num2);
return 0;
}
}
public delegate int TestHandler(int x);
private static void Test(int i)
{
<>c__DisplayClass1 class1 = new <>c__DisplayClass1();
class1.i = i;
TestHandler handler1 = new TestHandler(class1.<Test>b__0);
handler1(class1.i);
Console.WriteLine(class1.i);
}
private static void Main(string[] args)
{
Program.Test(0);
}
}看了反编译的结果,你会发现使用外部变量的成本很高,而且会带来潜在的危险,因此不推荐大规模使用。
5. 委托类型的推断
C# 2.0 允许我们在进行委托实例化时,省略掉委托类型,而直接采用方法名,C#编译器会做合理的推断。
public delegate void TestHandler(int x);
public class Program
{
static void Test(int i)
{
}
static void Execute(TestHandler test, int i)
{
}
static void Main(string[] args)
{
// C# 1.x 代码
TestHandler t1 = new TestHandler(Test);
Execute(new TestHandler(Test), 10);
// C# 2.0 代码
TestHandler t2 = Test; // 注意Test不包含括号,否则就成方法调用了。
Execute(Test, 20);
}
}看看反编译后的Main方法中 2.0 的代码样子。
private static void Main(string[] args)
{
TestHandler handler1 = new TestHandler(Program.Test);
Program.Execute(new TestHandler(Program.Test), 20);
}呵呵,还是偷懒无敌啊。
[最后修改由 yuhen, 于 2010-05-31 12:47:06]
评论Feed: http://www.rainsts.net/feed.asp?q=comment&id=111
这篇日志没有评论。






