3.10 委托和事件
委托让初学者觉得非常的疑惑和困难,委托其实就是一种引用方法的类型。委托通常与事件一起使用。通常情况下,如果为委托分配了方法,委托和声明的方法具有完全相同的功能,在ASP.NET应用程序的开发中,服务器控件就使用了委托和事件的思想进行了开发。
3.10.1 委托
委托的方法和其他所有的方法一样,具有参数以及返回值,委托用关键字delegate修饰,示例代码如下所示。
委托是一种安全的类型,并将方法安全的封装。在C/C++应用程序开发中,C#中的委托和C++的函数的指针类型类似,而稍有不同的是,C#中的委托是面向对象、类型安全的。委托的类型由委托的名称定义,如上述代码所示,代码中声明了MySound委托。
在委托对象被创建的时候,通常情况下提供委托包装的方法或匿名方法。实例化委托后,委托将把对它进行的方法调用传递给方法,委托允许将方法作为参数进行传递并定义回调的方法,调用方传递给委托的参数被传递给方法,相应的委托的方法的返回值返回给调用方,示例代码如下所示。
当创建委托并实例化委托对象时,委托把对它进行的方法调用传递方法。上述代码中,将DelegateMethod方法传递,在实例化后,当使用委托时,调用方(del)传递给委托的参数(Delegate Method)被传递给了方法(DelegateMethod),实现了方法,并调用、执行方法,运行结果如图3-7所示。
图3-7 委托的定义和使用
3.10.2 声明事件
在3.2.3小结中,讲解了事件的基本概念,事件具有以下特点:
?1)事件通常使用委托事件处理程序进行声明。
?2)事件始终通知对象消息并指示需要执行某种操作的一种方式。
?3)发行者确定何时引发事件,订阅者确定执行何种操作来响应该事件。
?4)一个事件可以有多个订阅者。一个订阅者可处理来自多个发行者的多个事件。
?5)没有订阅者的事件永远不会被调用。
?6)事件通常用于通知用户操作,例如,图形用户界面中的按钮单击或菜单选择操作。
?7)如果一个事件有多个订阅者,当引发该事件时,会同步调用多个事件处理程序,也可以使用异步处理多个事件。
事件和方法一样,通常事件和方法一起使用,事件和委托一样具有签名,但是事件的签名通过委托类型来定义,示例方法代码如下所示。
上述代码中,声明了一个委托,声明委托后,在Event类中声明了一个事件,这个事件绑定到委托MyDel。在事件的签名中,第一个参数为引用事件源的对象,第二个参数为一个传送与事件相关的数据的类。在C#中,规范的代码编写能够让代码更具可读性。
3.10.3 引发事件
如果要引发事件,类可以调用委托,并传递所有与事件有关的参数。上面的章节中讲到,事件通常和委托一起使用,并且通过给委托发送信息,来引发事件。如果该事件没有任何处理程序,则此事件为空,所以在引发事件之前,必须先确定该事件不为空,否则会抛出NullReferenceException异常,引发事件代码如下所示。
每一个事件都可以分配多个程序来接收该事件,这样,事件自动调用每个接收器,当有多个接收器时,引发事件只需要调用一次事件。
3.10.4 订阅事件
事件可以像一个方法一样,若要接收某个事件的类,可以创建一个方法来接收该事件,接收事件的类像类事件自身添加方法的委托,这个被称作“订阅事件”,可以说,和平时上网中的RSS订阅整个过程很像。值得注意的是,接收器必须具有与事件自身相同的签名的方法,然后该方法才能采取适当的操作来响应事件,示例代码如下所示。
每一个事件可以分配多个程序来接收该事件,也就是说可以有多个接收器。多个接收器由源按照顺序调用。如果一个接收出现异常,则没有接收的接收器会接收事件。如果要订阅事件,接收器必须创建一个与事件具有同种类型的委托,并使用事件处理委托的目标,这也就是为什么事件通常情况下会与委托一起使用。示例代码如下所示。
上述代码中,通过“+=”运算符订阅了一个事件,同样,也可以使用“-=”号取消订阅。示例代码如下所示。
3.10.5 委托和事件
上面几节中分开讲解委托和事件,对于初学者而言,委托和事件是很难学习的知识,但是当学习过委托和事件之后,会发现委托和事件非常的简单。在ASP.NET开发当中,很多控件都使用了委托和事件。例如当单击一个按钮控件时,按钮会发送信息指示“引发了一个按钮事件”,然后发送给相应的接收器,接收器接收了发来的信息从而引发相应的操作。在了解委托和事件的基本概念后,下列代码说明了怎样一步步的使用委托和事件。
为了实现广播喇叭功能(类似QQ的聊天窗口的系统信息),应用程序中不仅有用户的聊天窗口,也包括系统发送窗口。系统可以给用户的聊天窗口发送系统信息,在应用程序中,不仅需要广播用户的信息,同样系统也能够广播系统信息。为了实现这一功能,首先,需要创建一个委托,示例代码如下所示。
在创建了委托后,就要为写方法,示例代码如下所示。
注意:在上述代码中,del是一个委托变量,del(str)会按照方法的签名在委托的方法表中执行。上述代码,与del(string str)签名相同的方法有Output和SystemOutput,他们的方法签名相同。
在主函数中,可以通过委托来使用方法,示例代码如下所示。
上述代码中,使用了OutputChoose方法。值得注意的是,在OutputChoose方法中,其中的一个参数是方法名称。因为通过委托,可以将方法名称作为参数进行传递,从而执行了相应的方法。值得注意的是,在上述代码中,委托等方法全部都声明在一个类中,因为这样能够方便理解,但是这样就不具备面向对象的特点,面向对象的特性就是封装,封装能让代码具有结构性,于是可以使用事件。创建一个类,类名称叫OutputChoose,示例代码如下所示。
上述代码将前面代码中的方法进行了封装作为委托。然后添加一个用户消息类,类名为UserMessage,示例代码如下所示。
再添加一个系统消息类,类名称为SystemMessage,示例代码如下所示。
在主函数中,可以触发事件来,示例代码如下所示。
上述代码中,OnLoad()触发了之前注册的事件,并执行事件,运行结果如图3-8所示。
图3-8 委托和事件的综合用例
运行结果显示,当创建了一个对象,对象可以注册声明事件,因为该对象没有实现该事件的方法的具体实现,但是在事件中增加了方法,类似于在该类中增加了一个方法,而在该类的编码实现中,定义了一个OnLoad方法来调用所有注册对象的方法。
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();//等待用户按键
}
}
{
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("事件被使用");//输出字串
}
}
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());//不为空则使用委托
}
}
}
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() + "引发了一个事件");//执行方法体
}
}
{
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;//增加事件
}
{
MyDel del = new MyDel(EventTestReceiver);//声明委托
eve.EventTest += del;//增加事件
}
上述代码中,通过“+=”运算符订阅了一个事件,同样,也可以使用“-=”号取消订阅。示例代码如下所示。
+展开
-C#
public void EventTestSubscribe(Event eve)
{
MyDel del = new MyDel(EventTestReceiver);//声明委托
eve.EventTest -= del;//取消事件
}
{
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);
}
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("你好", 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);//当存在事件时,调用所有注册对象的方法
}
}
}
}
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);//简单的输出
}
}
}
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);//显式系统发送的消息
}
}
}
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();
}
{
OutputChoose opc = new OutputChoose();//声明一个类的对象
SystemMessage msg = new SystemMessage();//注册方法
opc.OnLoad(); //开始自动调用所有注册的方法
Console.ReadKey();
}
上述代码中,OnLoad()触发了之前注册的事件,并执行事件,运行结果如图3-8所示。
图3-8 委托和事件的综合用例
运行结果显示,当创建了一个对象,对象可以注册声明事件,因为该对象没有实现该事件的方法的具体实现,但是在事件中增加了方法,类似于在该类中增加了一个方法,而在该类的编码实现中,定义了一个OnLoad方法来调用所有注册对象的方法。
加支付宝好友偷能量挖...