Kevin Shan

Delegate在C#中的语法甜头(syntactic sugar)
在享受着C#优雅的语法的时候,你还能记起为实现这种优雅语法而默默工作的后台编译器吗?
本文就Delegate在C#中的语法甜头(syntactic sugar)详细讲述了为实现这些语法甜头,编译器做出的辛勤而卓越工作。

首先,我们先简要介绍一下Delegate,下面的代码定义了一个Action的delegate,匹配无参的没有返回值的方法。

using System;

 

namespace DelegateDemo

{

    class Program

    {

        static void Main(string[] args)

        {

        }

    }

 

    delegate void Action();

}

编译上面的代码,用ILDasm查看。可以看出Action其实是一个继承自System.MulticastDelegate的标记为sealed的class,只是C#编译器自动为我们生成了这样的代码(甜头1)。这个class有四个方法,分别为构造函数,同步Invoke方法,和异步的BeginInvoke,EndInvoke方法。



下面的代码演示了delegate的用法。我们构造了一个delegate实例eat,然后像调用方法一样直接调用eat就执行了它。


using System;

 

namespace DelegateDemo

{

    class Program

    {

        static void Main(string[] args)

        {

            Action eat = new Action(Eat);

            eat();

            Console.ReadKey();

        }

 

        static void Eat()

        {

            Console.WriteLine("Eat!");

        }

    }

 

    delegate void Action();

}


看看下面的代码,实际上C#编译器知道eat是一个delegate,所以它生成的是调用eat的Invoke()方法(甜头2)。



实际上,我们可以这样显示的写调用Invoke()的代码。

            Action eat = new Action(Eat);

            eat.Invoke();


我们可以使用+=和-=来绑定多个方法。当然,这是操作符重载。如果你看IL代码,你会发现其实是调用了Combine方法来绑定两个方法。(甜头3)

            Action eat = new Action(Eat);

            eat += new Action(Eat);

            eat();




在.NET2.0中,我们还可以这样书写。即使用匿名代理。很多情况下,这样会简化我们的代码。(甜头4)。

            Action eat = new Action(Eat);

            eat += delegate() { Console.WriteLine("Eat more!"); };

            eat();


事实上,这又是编译器在幕后做的工作,它为我们生成了一个private的匿名方法,是否为static得看你方法内有没有调用实例字段。



更有用的是,我们甚至可以更简化上面的代码。

            Action eat = new Action(Eat);

            eat += delegate { Console.WriteLine("Eat more!"); };

            eat();


我们去掉了delegate后面的参数列表。如果你在这个这个匿名代理中不需要使用参数的话,完全可以去掉。在这个例子中虽然我们只取得了一个括号,但是想想事件的参数列表吧,我们不用每次都写(object sender, EventArgs e)这样的代码是多么的爽。(甜头5)


还有一个更重要的甜头(甜头6),我们可以在匿名代理里面直接使用其代码块所在的方法的临时变量和方法参数。

        static void Main(string[] args)

        {

            string name = "apple";

            Action eat = new Action(Eat);

            eat += delegate { Console.WriteLine("Eat " + name + args.ToString()); };

            eat();

 

            Console.ReadKey();

        }


在这个例子中,编译器做的要更多。它会给我们生成一个class来包装需要的参数和动态生成的方法。




这是非常有用的,想想如果我们自己来实现这样的参数传递的话,代码将会多么复杂和难以维护。

事实上,编译器在背后默默的做很多的事情。 给我们编程带来了很多方便。这里只是列举了C#编译器在delegate上做的一些事情,当然并不是全部。

――Kevin Shan

posted on 2007-02-01 13:33  Kevin Shan  阅读(7754)  评论(11编辑  收藏  举报