C#学习篇之基础回顾(14)----- 委托和事件

委托

委托是一个类,它定义了方法的类型,可以将方法作为另一个方法的参数进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If-else(Switch)语句,同时使得程序具有更好的可拓展性。

委托的声明格式: public delegate void 委托名(参数类型 参数);

我们先举个例子,假设我们遇到朋友要问好,如早上好,但是外国友人可能听不懂中文,需要用对应的语言进行沟通,如使用英语,所以要打招呼就需要知道对方的名字以及他的语言,我们可以使用方法来实现:

     class Program
    {
        public static void GreetPeople(string name,string language)
        {
            if(language == "Chinese")
            {
                GreetChinese(name);
            }
            else if(language == "English")
            {
                GreetEnglish(name);
            }
        }
        public static void GreetChinese(string name)
        {
            Console.WriteLine("早上好," + name);
        }
        public static void GreetEnglish(string name)
        {
            Console.WriteLine("Good Morning," + name);
        }

        static void Main(string[] args)
        {
            GreetPeople("小赵","Chinese");
            GreetPeople("York","English");
            Console.ReadKey();
        }
    }

运行结果:早上好,小赵

                             Good Morning,York

OK,方法很简单,实现了对不同国家的人问好,但是如果遇到的人很多而且来自不同国家,我们是不是还要在GreetPeople中使用if-else进行不断判断,这样就会牵一发而动全身,并且可拓展性会非常差。

我们再回来仔细看GreetPeople方法的内容,发现我们是通过判断language来选择不同的方法,我们能不能直接跳过这个步骤呢?直接将GreetChinese方法以及GreetEnglish方法作为参数放到GreetPeople岂不是很方便,这样我们添加更多的方法也不用修改GreetPeople,只需要传进去不同参数就可以了,这其实就已经是委托事件了,我们可以看一下实现方式:

     //定义委托事件
    public delegate void GreetingDelegate(string name);
    class Program
    {
        public static void GreetPeople(string name,GreetingDelegate MakeGreet)
        {
            MakeGreet(name);
        }

        public static void GreetChinese(string name)
        {
            Console.WriteLine("早上好," + name);
        }
        public static void GreetEnglish(string name)
        {
            Console.WriteLine("Good Morning," + name);
        }

        static void Main(string[] args)
        {
            GreetPeople("小王", GreetChinese);
            GreetPeople("York", GreetEnglish);
            Console.ReadKey();
        }
     }

运行结果:早上好,小王

                  Good Morning,York

是不是很简单,我们回头看定义,委托是一个类,它定义了方法的类型,可以将方法作为参数进行传递,如string对于字符串。

聪明的你可能已经想到了,我们可以像string一样直接定义一个委托变量,如:

    static void Main(string[] args)
     {
            GreetingDelegate greet1 = GreetChinese;
            GreetingDelegate greet2 = GreetEnglish;
            GreetPeople("小王",greet1);
            GreetPeople("York", greet2);
            Console.ReadKey();
     }

如你所料,这样也是正确的,这里我想说的是委托不同于string的一个特性是:可以将多个方法赋予同一个委托,当调用该委托时可以依次调用其它所绑定的方法。如:

    static void Main(string[] args)
    {
            GreetingDelegate greet1 = GreetChinese;
            greet1 += GreetEnglish;
            GreetPeople("小王",greet1);
            Console.ReadKey();
    }

运行结果:早上好,小王

                  Good Morning,小王

这里需要注意的是,委托需要先通过“=”进行赋值,才能够使用“+=”进行绑定多个方法,否则会出现“使用了未赋值的局部变量”的编译错误。

如果委托绑定了多个方法,再重新赋值为null或者其他方法,则会将前面的方法全部清空。

委托实例化,我们可以直接绕过GreetPeople。如:

     static void Main(string[] args)
    {
            GreetingDelegate greet1 = new GreetingDelegate(GreetChinese);
            greet1 += GreetEnglish;
            greet1("小王");
            Console.ReadKey();
    }

运行结果:早上好,小王

                 Good Morning,小王

委托总结

(1)委托是一个类,它定义了方法的类型,可以将方法作为另一个方法的参数进行传递。

(2)委托变量可以绑定多个方法,当调用此变量时,可以依次调用所有绑定的方法。

(3)委托变量可以通过“+=”增加绑定,通过“-=”解除绑定。

事件

继续上面的例子,在实际应用中GreetPeople应该放在一个单独的类中,然后对代码进行修改,修改之后变为:

    public delegate void GreetingDelegate(string name);
    class GreetManage
    {
        public GreetingDelegate delegate1;
        public void GreetPeople(string name)
        {
            if (delegate1 != null)
            {
                delegate1(name);
            }
        }
    }
    class Program
    {
        public static void GreetChinese(string name)
        {
            Console.WriteLine("早上好," + name);
        }
        public static void GreetEnglish(string name)
        {
            Console.WriteLine("Good Morning," + name);
        }
        static void Main(string[] args)
        {
            GreetManage g = new GreetManage();
            g.delegate1 = GreetChinese;
            g.GreetPeople("小钢");
            Console.ReadLine();
        }
     }

声明委托就是为了将其暴露在类的客户端进行方法的注册,所以只能声明为Public。同时我们也可以看到,在客户端可以对委托进行任意赋值的操作,会严重破坏对象的封装性。 类似于声明为Public的string类型,我们可以使用属性对字段进行封装,那么委托是否也能够进行封装呢?当然是可以的,所以我们就需要用到Event(事件),它可以封装委托类型的变量。

事件的声明格式: public event 委托名 事件名;

我们从声明格式可以看到,事件跟委托是分不开的,其实事件也非常好理解,无非就是声明了一个进行了封装的委托类型的变量

如:

//先声明委托类型
public delegate void BoilerLogHandler(string status);
//声明基于上面的委托定义事件
public event BoilerLogHandler BoilerEvent;

我们修改GreetManage类:

    class GreetManage
    {
        public event GreetingDelegate MakeGreet;
        public void GreetPeople(string name)
        {
            MakeGreet(name);
        }
    }

然后修改Main方法:

    static void Main(string[] args)
    {
            GreetManage g = new GreetManage();
            g.MakeGreet = GreetEnglish;//该行会报错
            g.MakeGreet += GreetChinese;
            g.GreetPeople("小钢");
            Console.ReadLine();
    }

这里我们会发现报错,说明事件无法进行赋值,只能通过+=或-=进行方法绑定。从这里我们可以看出事件能够保护对象的封装性。

为什么要使用事件而不是委托变量

除了事件能够保护对象的封装性,还有最重要的一点是:事件应由发布者触发,而不应该由客户端来触发。

Ps:事件中各模块可以分为发布者(publisher)订阅者(subscriber)客户端(client)

    GreetingDelegate greet1 = new GreetingDelegate(GreetChinese);
    greet1 += GreetEnglish;
    greet1("小王");

我们可以看到前面的例子,发现委托可以在客户端中越过发布者,直接触发订阅者的响应事件。其实这是非常不合理的,因为你直接使用委托变量触发事件,会影响到所有已经注册了该事件的订阅者

我们再来探究下事件的定义,事件是对象用于(向系统中的所有相关组件)广播已发生事情的一种方式。任何其他组件都可以订阅事件,并在事件引发时得到通知。简单地说,就比如用户的操作如按键、移动鼠标等,应用程序需要在事件发生时响应事件。

由此可见,事件应该是由事件发布者在其本身的某个行为中触发的,只能被其他组件订阅,而不能在客户端中直接触发事件。所以我们就需要用event关键字来发布事件,能够禁止在客户端直接触发事件,只能通过调用发布者的方法来触发事件。如:

    GreetManage g = new GreetManage();
    g.delegate1 = GreetChinese;
    g.GreetPeople("小钢");

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值