C# 匿名方法
[ C# 2.0/.NET 2.0 新增特性 ]
2.1 从委托的声明说起
C#中的匿名方法是在C#2.0引入的,它终结了C#2.0之前版本声明委托的唯一方法是使用命名方法的时代。不过,这里我们还是看一下在没有匿名方法之前,我们是如何声明委托的。
(1)首先定义一个委托类型:
public delegate void DelegateTest(string testName);
(2)编写一个符合委托规定的命名方法:
public void TestFunc(string name) { Console.WriteLine("Hello,{0}", name); }
(3)最后声明一个委托实例:
DelegateTest dgTest = new DelegateTest(TestFunc); dgTest("Edison Chou");
(4)调试运行可以得到以下输出:
由上面的步凑可以看出,我们要声明一个委托实例要为其编写一个符合规定的命名方法。但是,如果程序中这个方法只被这个委托使用的话,总会感觉代码结构有点浪费。于是,微软引入了匿名方法,使用匿名方法声明委托,就会使代码结构变得简洁,也会省去实例化的一些开销。
2.2 引入匿名方法
(1)首先,我们来看看上面的例子如何使用匿名方法来实现:
DelegateTest dgTest2 = new DelegateTest(delegate(string name) { Console.WriteLine("Good,{0}", name); });
从运行结果图中可以看出,原本需要传递方法名的地方我们直接传递了一个方法,这个方法以delegate(参数){方法体}的格式编写,在{}里边直接写了方法体内容。于是,我们不禁欢呼雀跃,又可以简化一些工作量咯!
(2)其次,我们将生成的程序通过Reflector反编译看看匿名方法是怎么帮我们实现命名方法的效果的。
①我们可以看到,在编译生成的类中,除了我们自己定义的方法外,还多了两个莫名其妙的成员:
②经过一一查看,原来编译器帮我们生成了一个私有的委托对象以及一个私有的静态方法。我们可以大胆猜测:原来匿名方法不是没有名字的方法,还是生成了一个有名字的方法,只不过这个方法的名字被藏匿起来了,而且方法名是编译器生成的。
③经过上面的分析,我们还是不甚了解,到底匿名方法委托对象在程序中是怎么体现的?这里,我们需要查看Main方法,但是通过C#代码我们没有发现一点可以帮助我们理解的。这时,我们想要刨根究底就有点麻烦了。还好,在高人指点下,我们知道可以借助IL(中间代码)来分析一下。于是,在Reflector中切换展示语言,将C#改为IL,就会看到另外一番天地。
(3)由上面的分析,我们可以做出结论:编译器对于匿名方法帮我们做了两件事,一是生成了一个私有静态的委托对象和一个私有静态方法;二是将生成的方法的地址存入了委托,在运行时调用委托对象的Invoke方法执行该委托对象所持有的方法。因此,我们也可以看出,匿名方法需要结合委托使用。
2.3 匿名方法扩展
(1)匿名方法语法糖—更加简化你的代码
在开发中,我们往往会采用语法糖来写匿名方法,例如下面所示:
DelegateTest dgTest3 = delegate(string name) { Console.WriteLine("Goodbye,{0}", name); }; dgTest3("Edison Chou");
可以看出,使用该语法糖,将new DelegateTest()也去掉了。可见,编译器让我们越来越轻松了。
(2)传参也有大学问—向方法中传入匿名方法作为参数
①在开发中,我们往往声明了一个方法,其参数是一个委托对象,可以接受任何符合委托定义的方法。
static void InvokeMethod(DelegateTest dg) { dg("Edison Chou"); }
②我们可以将已经定义的方法地址作为参数传入InvokeMethod方法,例如:InvokeMethod(TestFunc); 当然,我们也可以使用匿名方法,不需要单独定义就可以调用InvokeMethod方法。
InvokeMethod(delegate(string name) { Console.WriteLine("Fuck,{0}", name); });
(3)省略省略再省略—省略"大括号"
经过编译器的不断优化,我们发现连delegate后边的()都可以省略了,我们可以看看下面一段代码:
InvokeMethod(delegate { Console.WriteLine("I love C sharp!"); });
而我们之前的定义是这样的:
public delegate void DelegateTest(string testName); static void InvokeMethod(DelegateTest dg) { dg("Edison Chou"); }
我们发现定义时方法是需要传递一个string类型的参数的,但是我们省略了deletegate后面的括号之后就没有参数了,那么结果又是什么呢?经过调试,发现结果输出的是:I love C sharp!
这时,我们就有点百思不得其解了!明明都没有定义参数,为何还是满足了符合委托定义的参数条件呢?于是,我们带着问题还是借助Reflector去一探究竟。
①在Main函数中,可以看到编译器为我们自动加上了符合DelegateTest这个委托定义的方法参数,即一个string类型的字符串。虽然,输出的是I love C sharp,但它确实是符合方法定义的,因为它会接受一个string类型的参数,尽管在方法体中没有使用到这个参数。
②刚刚在Main函数中看到了匿名方法,现在可以看看编译器为我们所生成的命名方法。
出处:http://edisonchou.cnblogs.com