君子博学而日参省乎己 则知明而行无过矣

博客园 首页 新随笔 联系 订阅 管理

托和事件在 .NET Framework中的应用非常广泛,然而,较好地理解委托和事件对很多刚接触C#的人来说并不容易。它们就像一道坎儿,过了这个槛的人,觉得真是太容易了,而没有过去的人每次见到委托和事件就觉得心里迷糊,浑身不自在。下面就通过简洁的语言和通俗的例子来讲解一下什么是委托、如何实现委托,如何使用委托,以及事件的使用与处理。

1.示例代码

      (示例位置:光盘"code"ch01"11

public class ClassPeople

{

    public void SayChinese(string name)

    {

        Console.WriteLine("你好," + name);

    }

    public void DoWork(string name)

    {

        SayChinese(name);//传字符串参数           

    }

}

class Program

{

    static void Main(string[] args)

    {

        ClassPeople cp = new ClassPeople();

        cp.DoWork("李天平");

        System.Console.ReadLine();

    }

}

 

在这段代码中,我们通过把字符串作为参数传给DoWork()方法输出问候信息。例如,我们传递字符串“李天平”进去,在这个方法中,将调用SayChinese方法,向屏幕输出“你好,李天平”。

2.通过条件判断进行扩展

现在假设这个程序需要进行全球化,但是,外国人看不懂“你好”是什么意思,怎么办呢?好吧,我们再加个英文版的问候方法:

     (示例位置:光盘"code"ch01"12

 

public class ClassPeople

{

    public void SayChinese(string name)

    {

        Console.WriteLine("你好," + name);

    }

    public void SayEnglish(string name)

    {

        Console.WriteLine("Hello," + name);

    }

    public void DoWork(string name)

    {

        SayChinese(name);//传字符串参数

    }

}

class Program

{

    static void Main(string[] args)

    {

        ClassPeople cp = new ClassPeople();         

        cp.DoWork("李天平");

        Console.ReadLine();

    }

}

 

虽然加了英文版的问候方法,但是,DoWork方法并不知道什么时候该调用哪个方法进行输出。所以,在调用之前,我们还需要再定义一个枚举来进行判断:

(示例位置:光盘"code"ch01"13

using System;

namespace ConsoleApplication1

{

    public class ClassPeople

    {

        public void SayChinese(string name)

        {

            Console.WriteLine("你好," + name);

        }

        public void SayEnglish(string name)

        {

            Console.WriteLine("Hello," + name);

        }

        public enum Language

        {

            English, Chinese

        }

        public void DoWork(string name, Language lang)

        {           

            switch (lang)

            {

                case Language.English:

                    SayEnglish(name);

                    break;

                case Language.Chinese:

                    SayChinese(name);

                    break;

            }

        }

    }

    class Program

    {

        static void Main(string[] args)

        {

            ClassPeople cp = new ClassPeople();

            cp.DoWork("李天平", ClassPeople.Language.Chinese);

            cp.DoWork("litianping", ClassPeople.Language.English);           

            System.Console.ReadLine();

        }

    }

}

 

 

程序运行结果如图1-14所示。

1-14 条件判断输出结果

这样问题就解决了,可以根据传进来的语言枚举值和姓名字符串来输出相应语言的问候语了。不过,细想一下这个方法的可扩展性还是太差了,如果以后我们需要再添加韩文版、日文版,就不得不反复修改枚举和DoWork()方法,以适应新的需求。

那么,有没有更好的解决方案呢?

3.引入委托

我们可以先来看一下DoWork()方法的声明:

    public void DoWork(string name, Language lang)

我们可以看到,在这个方法里,我们传入了string name参数,string是参数类型,name是参数变量。我们赋给它“李天平”,它就把“李天平”这个值传进去;我们赋给它 litianping”,它就把“litianping”这个值传进去。然后,根据Language类型判断用哪个方法进行输出处理。

我们假设DoWork()可以接收一个参数变量,这个参数变量可以代表一个方法,那么当我们把这个参数变量赋值为SayEnglish()时,它就代表SayEnglish()方法去执行;当我们把这个参数变量赋值为SayChinese()时,它就代表SayChinese()方法去执行。我们将这个参数变量暂且命名为MakeSay,那么不是可以像给name赋值一样,在调用DoWork()方法的时候,给这个MakeSay参数也赋上值(SayEnglish或者SayChinese等)传递进去吗? 然后,我们在DoWork()方法内,也可以像使用别的参数一样使用MakeSay。由于MakeSay代表一个方法,所以它的使用方式应该和它被赋值的方法(SayEnglish或者SayChinese)是一样的,接收一个name参数。

例如:

       void MakeSay(string name)

这样的话,我们的DoWork()方法就会演变成如下:

public void DoWork(string name, *** MakeSay)

{

    MakeSay(name);             //传字符串参数

}

仔细看参数部分,这里的“***”这个位置,通常应该是参数的类型。但到目前为止,我们还不知道这个参数的类型应该是什么。但是我们可以看到如果把那两个方法当做参数传进去,就不需要再做枚举判断了,直接用传进来的方法输出问候语就可以了。这样是不是就完全解决了上面的问题了呢。

对,你现在应该可以明白了,这个解决的方法是可行的,这就是委托。

string定义了name参数所代表的值的类型,委托就是定义MakeSay参数所代表的方法的类型。

在C#中,定义委托的语法是:

     delegate void SayDelegate(string name);

delegate关键字用于声明一个引用类型,该引用类型可用于封装命名方法或匿名方法。

和上面的SayChinese(string name)或者SayEnglish(string name)方法定义对比一下,除了加入了delegate关键字以外,其余的完全一样。

现在,我们再回来看DoWork()方法,就会演变成如下:

        public void DoWork(string name,SayDelegate MakeSay)

        {

            MakeSay(name);       //传字符串参数

        }

如你所见,委托SayDelegate出现的位置与string一样,string是一个类型,那么SayDelegate应该也是一个类型,或者叫类(Class)。但是委托的声明方式和类完全不同,这是怎么回事呢?实际上,委托在编译的时候确实会编译成类。因为Delegate是一个类,所以在任何可以声明类的地方都可以声明委托。

现在,让我们看一下这个范例的完整代码:

    (示例位置:光盘"code"ch01"14

using System;

namespace ConsoleApplication1

{

    public delegate void SayDelegate(string name);

    public class ClassPeople

    {       

        public void SayChinese(string name)

        {

            Console.WriteLine("你好," + name);

        }

        public void SayEnglish(string name)

        {

            Console.WriteLine("Hello," + name);

        }

        //注意此方法,它接受一个SayDelegate类型的方法作为参数

        public void DoWork(string name, SayDelegate MakeSay)

        {

            MakeSay(name);

        }

    }

    class Program

    {

        static void Main(string[] args)

        {

            ClassPeople cp = new ClassPeople();

            cp.DoWork("李天平", cp.SayChinese);

            cp.DoWork("litianping", cp.SayEnglish);

            System.Console.ReadLine();

        }

    }

}

 

输出结果和枚举的方式相同,但具有更好的扩展性,如图1-15所示。

1-15 委托输出结果

4.委托总结

委托是一种特殊的对象类型,它定义了方法的类型,使得可以将方法当做另一个方法的参数来进行传递,其特殊之处在于,我们以前定义的所有对象都包含数据,而委托包含的只是方法的地址。这种将方法动态地赋给参数的做法,可以避免在程序中大量使用ifelseswitch语句,同时使得程序具有更好的扩展性。

 

 

 

趣味理解

以前,公司过年过节,总会发一 些福利(如奖金、礼品等),都是把实实在在的物质给大家(相当于传具体类型参数值给方法)。今年全球金融危机,整个经济不景气,公司效益也不好,不能给大 家发物质的东西了。怎么办呢?老板眼珠一转,心生一计,决定给大家发“任务”,例如,销售公司产品任务,谁销售多少公司产品,这个钱就归谁,这样,不但公 司减轻了负担,而且也解决了福利问题,一举两得啊。

但只是公司这么一说是不安全的,没有法律保证,所以需要一个新的东西来实现任务书。(这里的任务书就像委托)任务书只是一种特殊的福利类型,其特殊之处在于,我们以前拿到的所有东西都是具体的物质。而任务书包含的只是获得物质的任务的说明,需要通过另外的劳动才能得到。

posted on 2011-04-19 23:00  刺猬的温驯  阅读(160)  评论(0编辑  收藏  举报