3.10 委托和事件

委托让初学者觉得非常的疑惑和困难,委托其实就是一种引用方法的类型。委托通常与事件一起使用。通常情况下,如果为委托分配了方法,委托和声明的方法具有完全相同的功能,在ASP.NET应用程序的开发中,服务器控件就使用了委托和事件的思想进行了开发。

3.10.1 委托
委托的方法和其他所有的方法一样,具有参数以及返回值,委托用关键字delegate修饰,示例代码如下所示。
+展开
-C#
    public delegate string MySound(string sound);//声明一个委托

委托是一种安全的类型,并将方法安全的封装。在C/C++应用程序开发中,C#中的委托和C++的函数的指针类型类似,而稍有不同的是,C#中的委托是面向对象、类型安全的。委托的类型由委托的名称定义,如上述代码所示,代码中声明了MySound委托。

在委托对象被创建的时候,通常情况下提供委托包装的方法或匿名方法。实例化委托后,委托将把对它进行的方法调用传递给方法,委托允许将方法作为参数进行传递并定义回调的方法,调用方传递给委托的参数被传递给方法,相应的委托的方法的返回值返回给调用方,示例代码如下所示。
+展开
-C#
    class Program//应用程序主类
    {
        public delegate void MyDel(string message);//声明一个委托
        public static void DelegateMethod(string message)//声明调用委托
        {
            Console.WriteLine("message is: " + message);//输出字串
        }
        static void Main(string[] args)//应用程序入口方法
        {
            MyDel del = DelegateMethod; //注意这里初始化
            del("Delegate Method");//使用委托
            Console.ReadKey();//等待用户按键
        }
    }

当创建委托并实例化委托对象时,委托把对它进行的方法调用传递方法。上述代码中,将DelegateMethod方法传递,在实例化后,当使用委托时,调用方(del)传递给委托的参数(Delegate Method)被传递给了方法(DelegateMethod),实现了方法,并调用、执行方法,运行结果如图3-7所示。
委托的定义和使用
图3-7 委托的定义和使用

3.10.2 声明事件
在3.2.3小结中,讲解了事件的基本概念,事件具有以下特点:
?1)事件通常使用委托事件处理程序进行声明。
?2)事件始终通知对象消息并指示需要执行某种操作的一种方式。
?3)发行者确定何时引发事件,订阅者确定执行何种操作来响应该事件。
?4)一个事件可以有多个订阅者。一个订阅者可处理来自多个发行者的多个事件。
?5)没有订阅者的事件永远不会被调用。
?6)事件通常用于通知用户操作,例如,图形用户界面中的按钮单击或菜单选择操作。
?7)如果一个事件有多个订阅者,当引发该事件时,会同步调用多个事件处理程序,也可以使用异步处理多个事件。
事件和方法一样,通常事件和方法一起使用,事件和委托一样具有签名,但是事件的签名通过委托类型来定义,示例方法代码如下所示。
+展开
-C#
        public delegate void MyDel(object sender, EventArgs e);//声明一个委托
        public class Event编写事件类
        {
            public event MyDel EventTest;//声明一个事件
            public void EventTestMethod() //编写事件方法
            {
                Console.WriteLine("事件被使用");//输出字串
            }
        }

上述代码中,声明了一个委托,声明委托后,在Event类中声明了一个事件,这个事件绑定到委托MyDel。在事件的签名中,第一个参数为引用事件源的对象,第二个参数为一个传送与事件相关的数据的类。在C#中,规范的代码编写能够让代码更具可读性。

3.10.3 引发事件
如果要引发事件,类可以调用委托,并传递所有与事件有关的参数。上面的章节中讲到,事件通常和委托一起使用,并且通过给委托发送信息,来引发事件。如果该事件没有任何处理程序,则此事件为空,所以在引发事件之前,必须先确定该事件不为空,否则会抛出NullReferenceException异常,引发事件代码如下所示。
+展开
-C#
        public delegate void MyDel(object sender, EventArgs e);//创建委托
        public class Event//编写事件类
        {
            public event MyDel EventTest;//声明一个事件
            public void EventTestMethod()//声明一个事件所执行的方法
            {
                MyDel OnLoad = EventTest;//声明事件的方法
                if (OnLoad != null)//判断事件是否为空
                {
                    OnLoad(this, EventArgs());//不为空则使用委托
                }
            }
        }

每一个事件都可以分配多个程序来接收该事件,这样,事件自动调用每个接收器,当有多个接收器时,引发事件只需要调用一次事件。

3.10.4 订阅事件
事件可以像一个方法一样,若要接收某个事件的类,可以创建一个方法来接收该事件,接收事件的类像类事件自身添加方法的委托,这个被称作“订阅事件”,可以说,和平时上网中的RSS订阅整个过程很像。值得注意的是,接收器必须具有与事件自身相同的签名的方法,然后该方法才能采取适当的操作来响应事件,示例代码如下所示。
+展开
-C#
        public class EventReceiver//创建一个接收器
        {
            public void EventTestReceiver(object sender, EventArgs e)//方法的签名必须相同
            {
                Console.WriteLine("从" + sender.ToString() + "引发了一个事件");//执行方法体
            }
        }

每一个事件可以分配多个程序来接收该事件,也就是说可以有多个接收器。多个接收器由源按照顺序调用。如果一个接收出现异常,则没有接收的接收器会接收事件。如果要订阅事件,接收器必须创建一个与事件具有同种类型的委托,并使用事件处理委托的目标,这也就是为什么事件通常情况下会与委托一起使用。示例代码如下所示。
+展开
-C#
            public void EventTestSubscribe(Event eve)
            {
                MyDel del = new MyDel(EventTestReceiver);//声明委托
                eve.EventTest += del;//增加事件
            }

上述代码中,通过“+=”运算符订阅了一个事件,同样,也可以使用“-=”号取消订阅。示例代码如下所示。
+展开
-C#
            public void EventTestSubscribe(Event eve)
            {
                MyDel del = new MyDel(EventTestReceiver);//声明委托
                eve.EventTest -= del;//取消事件
            }


3.10.5 委托和事件
上面几节中分开讲解委托和事件,对于初学者而言,委托和事件是很难学习的知识,但是当学习过委托和事件之后,会发现委托和事件非常的简单。在ASP.NET开发当中,很多控件都使用了委托和事件。例如当单击一个按钮控件时,按钮会发送信息指示“引发了一个按钮事件”,然后发送给相应的接收器,接收器接收了发来的信息从而引发相应的操作。在了解委托和事件的基本概念后,下列代码说明了怎样一步步的使用委托和事件。

为了实现广播喇叭功能(类似QQ的聊天窗口的系统信息),应用程序中不仅有用户的聊天窗口,也包括系统发送窗口。系统可以给用户的聊天窗口发送系统信息,在应用程序中,不仅需要广播用户的信息,同样系统也能够广播系统信息。为了实现这一功能,首先,需要创建一个委托,示例代码如下所示。
+展开
-C#
        public delegate void BetaDel string str);//创建一个委托

在创建了委托后,就要为写方法,示例代码如下所示。
+展开
-C#
        public delegate void BetaDel(string str);
        public static void Output(string str)//用户发送信息方法
        {
            Console.WriteLine("用户发送给你一个消息");//输出用户提示信息
        }
        public static void SystemOutput(string str)//系统发送信息方法
        {
            Console.WriteLine("系统发送给你一个消息");//输出用户提示信息
        }
        public static void OutputChoose(string str,BetaDel del)//使用委托变量
        {
            del(str);
        }

注意:在上述代码中,del是一个委托变量,del(str)会按照方法的签名在委托的方法表中执行。上述代码,与del(string str)签名相同的方法有Output和SystemOutput,他们的方法签名相同。

在主函数中,可以通过委托来使用方法,示例代码如下所示。
+展开
-C#
        static void Main(string[] args)
        {
            OutputChoose("你好", Output);//通过传递方法名称来使用方法
            Console.ReadKey();
        }

上述代码中,使用了OutputChoose方法。值得注意的是,在OutputChoose方法中,其中的一个参数是方法名称。因为通过委托,可以将方法名称作为参数进行传递,从而执行了相应的方法。值得注意的是,在上述代码中,委托等方法全部都声明在一个类中,因为这样能够方便理解,但是这样就不具备面向对象的特点,面向对象的特性就是封装,封装能让代码具有结构性,于是可以使用事件。创建一个类,类名称叫OutputChoose,示例代码如下所示。
+展开
-C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;//使用系统命名空间
namespace beta
{
    class OutputChoose
    {
        public string message="你有新短消息,请注意查收";//声明短消息字串
        public delegate void BetaDel(string str);//定义委托注册事件
        public event BetaDel MyEvent;//声明事件
        public void OnLoad()//编写OnLoad方法注册事件
        {
            if (MyEvent != null)
            {
                MyEvent(message);//当存在事件时,调用所有注册对象的方法
            }
        }
    }
}

上述代码将前面代码中的方法进行了封装作为委托。然后添加一个用户消息类,类名为UserMessage,示例代码如下所示。
+展开
-C#
using System.Linq;
using System.Text;//使用文本处理命名空间
namespace beta//声明当前程序命名空间
{
    class UserMessage
    {
        public void Output(string str)//输出方法
        {
            Console.WriteLine("用户发送给你一个消息:" + str);//简单的输出
        }
    }
}

再添加一个系统消息类,类名称为SystemMessage,示例代码如下所示。
+展开
-C#
using System.Linq;//使用文本处理命名空间
namespace beta//声明当前程序命名空间
{
    class SystemMessage
    {
        public void SystemOutput(string str)//系统获取输出方法
        {
            Console.WriteLine("系统发送给你一个消息:" + str);//显式系统发送的消息
        }
    }
}

在主函数中,可以触发事件来,示例代码如下所示。
+展开
-C#
        static void Main(string[] args)
        {
            OutputChoose opc = new OutputChoose();//声明一个类的对象
            SystemMessage msg = new SystemMessage();//注册方法
            opc.OnLoad(); //开始自动调用所有注册的方法
            Console.ReadKey();
        }

上述代码中,OnLoad()触发了之前注册的事件,并执行事件,运行结果如图3-8所示。
委托和事件的综合用例
图3-8 委托和事件的综合用例
运行结果显示,当创建了一个对象,对象可以注册声明事件,因为该对象没有实现该事件的方法的具体实现,但是在事件中增加了方法,类似于在该类中增加了一个方法,而在该类的编码实现中,定义了一个OnLoad方法来调用所有注册对象的方法。

加支付宝好友偷能量挖...


评论(0)网络
阅读(137)喜欢(0)Asp.Net/C#/WCF