using System;
using System.Collections.Generic;
using System.Text;
/// <summary>
/// 功能:委托链的测试
/// 异常:无
/// 作者:FengWei
/// 日期:2007-06-23
/// </summary>
namespace Lesson63
{
}
using System;
using System.Collections.Generic;
using System.Text;
/// <summary>
/// 功能:委托的测试
/// 异常:无
/// 作者:FengWei
/// 日期:2007-06-23
/// </summary>
namespace Lesson63
{
}
using System;
using System.Collections.Generic;
using System.Text;
/// <summary>
/// 功能:多点传送(同一事件的处理采用多个方法实现)
/// 异常:无
/// 作者:FengWei
/// 日期:2007-06-23
/// </summary>
namespace Lesson63
{
Console.ReadLine();
础:
1. .net框架约定,所有保存事件信息的类型都应该继承自System.EventArgs,并且类型名称应该以EventArgs结尾;委托类型应该以EventHandler结束,回调方法原型应该有一个void返回值,并且接受两个参数, 第一个Object指向发送通知的对象;第二个参数继随自EventArgs类型,包括接受者需要的附加信息。
2.如果定义的事件没有传递 给事件接收者的附加信息,便不必定义新的委托,直接使用System.EventHandler,并将EventArgs.Empty传递给第2个参数即可
public delegate void EventHandler(Object sender, EventArgs e);
.net框架程序设计
最简间的事件与委托关联
public delegate void RequestHandler(string Url);
//定义委托类型的事件
public event RequestHandler RequestEvent;
//定义事件处理程序,即委托的回调方法
public void RequestMothed(string url)
{
//do what you want to do;
}
//将事件处理程序添加到委托链
RequestHandler handler = new RequestHandler(RequestMothed);
//将委托对象添加到事件链中,删除委托对象用 -=
RequestEvent += handler;
//引发事件,可将事件当一个方法看待,参数必须与前面声明的一致
RequestEvent(stringTest);
事件可用的修饰符:
Static Virtual Override Abstract
Static类似于字段,类的甩的对象共享静态事件,当引用这类事件时,必须用类名称而不是名称
一:登记事件
定义一个收邮件的类MailManager,在MailManager中定义一个收到邮件便触发的事件MailMSG,传真fax寻呼pager等对象在登记MailManager和MailMSG事件,当新电子邮件到达时,MailManager将通知发送给所有登记对象,这些对象按自己的方法处理。 开始代码.
{
//在MailManager类中定义MailMsgEventArgs类型
public class MailMsgEventAgrs:EventArgs
{
public MailMsgEventArgs(string from, string to, string subject, string body)
{
this.from=from;
this.to=to;
this.subject=subject;
this.body=body;
}
public readonly string from, to, subject, body;
}
//定义委托
public delegate void MailMsgEventHandler(Object sender, MailMsgEventArgs args);
//定义委托类型 的事件
public event MailMsgEventHandler MailMsg;
//此方法发出通知,即有邮件是通知FAX和PAGER ,Fax和PAGER类可以重写些方法
public virtual void OnMailMsg(MailMsgEventArgs e)
{
//判断是否有对象登记事件
if(MailMsg != null)
{
//如果有,则通知委托链表上的所有对象
MailMsg(this, e);
}
}
//此方法在新电子邮件到达到被触发
public void SimulateArrivingMsg(string from, string to, string subject, string body)
{
MailMsgEventArgs e = new MailMsgEventArgs(from, to, subject, body);
OnMailMsg(e);
}
}
其中 编辑器编译public event MailMsgEventHandler MailMsg;时,会产生
二:侦听事件
{
//将MailManager对象传递给构造器
public Fax(MailManager mm)
//将回调函数与事件关联
mm.MailMsg += new MailManager.MailMsgEventHandler(FaxMsg);
//回调函数
{
//sender 表示MailManagercf 对象,如果要和事件触发者通信,将用到此参数
//MailManager 对象希望提供的一些附加事件信息
//Fax内部的处理
Console.WriteLine("Form:{0}\n To:{1}\n Subject: {2}\n: {3}\n", e.from, e.to, e,subject, e.body);
}
public void Unregister(MailManager mm)
{
//构造一个指向FaxMsg回调方法垢MailMsgEventHandler委托实例
MailManager.MailMsgEventHandler callback = new MailManager.MalMsgEventHandler(FaxMsg);
//注销MailManager的MailMsg事件
mm.MailMsg -= callback;
}
}
其中,编辑器处理mm.MailMsg += new MailManager.MailMsgEventHandler(FaxMsg);
时会将它转换为
//调用了前面public event MailMsgEventHandler MailMsg编辑的产生的代码 public virtual void add_MailMsg()方法,所以此方法里面有一个是MailMsgEventHandler委托的实例。
三:显式控制事件注册,实际上就将public event MailMsgEventHandler MailMsg编辑时产生的内部代码源码化
{
//传给事件接受者的类型信息,类似于前的
public class MailMsgEventAgrs : EventArgs{..}
//定义委托
public delegate void MailMsgEventHandler(Object sender, MailMsgEventArgs args);
//变化发生在这里,和前面编辑器内部产生代码一样
private MailMsgEventHandler mailMesgEventHandlerDelegate;
//显式定义事件及其访问器的方法
public event MailMsgEventHandler MailMsg
{
add
{
mailMsgEventHandlerDelegate=(MailMsgEventHandler)Delegate.Combine(mailMsgEventHandlerDelegate, value);;
}
//将传入的事件处理器(value)从委托链表上移除
remove
{
mailMsgEventHandlerDelegate = (MailMsgEventHandler)Delegate.Remove(mailMsgEventHandlerDelegate, value);
}
}
//下面受保护的虚方法负责通知事件的登记对象,这里已经不是事件而是一个委托类型 的字段了
//有点难以理解
protected virtual void onMailMsg(MailMsgEventArgs e)
{
if(mailMsgEventHandlerDelegate != null)
{
mailMsgEventHandlerDelegate(this, e);
}
}
//在新电子邮件到达时被调用
public void SimulateArrivingMsg(string from, string to, string Subject, string body)
{.}
}
至于为什么要用委托,可以从上面看出,它是类型安全的,因为一个函数指针的非正常调用可能会有未知的实现。
事件的发生只会激发某个动作(如果有),而具体要做什么,这就是委托实例的调用列表中的方法的执行。两者从概念上讲没有共同点。所以你没区分清楚是因为还没有真正理解它们重要的用途。
委托链
委托的Invoke()方法,在委托链上,具有调用一个委托对象之前的委托对象(如果存在)的能力,这样,可以确保委托链上所有的委托对象都得到调用。但它的缺点是:只能取得最后一个调用对象的返回值,而前面调用对象的返回值都会丢失;另外,如果中间有一个委托对象产生异常或阻塞很长的时间,则会阻止其后面所有的委托对象的调用,因为委托链上的对象是按序调用的。
针对此种情况,MulticastDelegate类提供一个实例方法:GetInvocationList(),它返回一个委托形式的数组,从而可以任意调用委托链上的对象。
using System.Text;
namespace delegateStudy
{
class aClass
{
public string A()
{
Console.WriteLine("Write A");
return "this is A";
}
}
class bClass
{
public string B()
{
Console.WriteLine("Write B");
return "this is B";
}
}
class cClass
{
public string C()
{
Console.WriteLine("Write C");
return "this is C";
}
}
class delegateStudy
{
delegate string GetReturnValue(); //定义
static void Main()
{
//声明一个空委托
GetReturnValue getReturn = null;
//将上面三个方法添加到委托链上
getReturn += new GetReturnValue(new aClass().A);
getReturn += new GetReturnValue(new bClass().B);
getReturn += new GetReturnValue(new cClass().C);
Why(getReturn);
}
static void Why(GetReturnValue grv)
{
//委托为空则什么事也不做
if(grv == null) return;
//获取一个由委托链上对象组成的数据,用GetInvocationList()方法
Delegate[] arrayOfDelegate = grv.GetInvocationList();
foreach(GetReturnValue getR in arrayOfDelegate)
{
Console.WriteLine(getR());
}
//取得对应委托对象的回调方法名
Console.WriteLine(arrayOfDelegate[2].Method);
//输出对应委托对象回调方法属于哪个名称空间和类,这里输出为delegateStudy.bClass
Console.WriteLine(arrayOfDelegate[1].Target);
//调用arrayOfDelegate[0]位置委托对象的回调方法,传对应参数,没有参数为null
//执行了aClass里的A()
Console.WriteLine(arrayOfDelegate[0].DynamicInvoke(null));
Console.Read();
}
}
}