步步为营 C# 技术漫谈 五、事件与委托机制.docx_第1页
步步为营 C# 技术漫谈 五、事件与委托机制.docx_第2页
步步为营 C# 技术漫谈 五、事件与委托机制.docx_第3页
步步为营 C# 技术漫谈 五、事件与委托机制.docx_第4页
步步为营 C# 技术漫谈 五、事件与委托机制.docx_第5页
已阅读5页,还剩5页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

步步为营 C# 技术漫谈 五、事件与委托机制 概述 C#中的委托类似于C或C+中的函数指针。使用委托使程序员可以将方法引用封装在委托对象内。然后可以将该委托对象传递给可调用所引用方法的代码,而不必在编译时知道将调用哪个方法。与C或C+中的函数指针不同,委托是面向对象,而且是类型安全的。 C#中的“事件”是当对象发生某些事情时,类向该类的客户提供通知的一种方法。事件最常见的用途是用于图形用户界面;通常,表示界面中的控件的类具有一些事件,当用户对控件进行某些操作(如单击某个按钮)时,将通知这些事件。使用委托来声明事件。委托对象封装一个方法,以便可以匿名调用该方法。事件是类允许客户为其提供方法(事件发生时应调用这些方法)的委托的一种方法。事件发生时,将调用其客户提供给它的委托。 注明:委托是对方法的包装 在不确定要调用什么方法时候而又不能用抽象或者多态实现的时候用委托。 委托在Observer模式示例:先创建PilotLamp.cs:?12345678910111213141516public interface PilotLamp / / green light / void TurnOn(); / / notice / string Notice get; set; 再创建DelegateEvent.cs:?1public delegate void EventHandler();再创建TrafficLight.cs:?123456789101112131415161718192021222324252627public class TrafficLight : PilotLamp public event EventHandler Notices; private string notice; #region GreenLight 成员 public void TurnOn() if (Notices != null) Notices(); public string Notice get return notice; set notice = value; #endregion 再创建Driver.cs:?12345678910111213141516public class Driver private string Name; private PilotLamp greenLight; public Driver(string name, PilotLamp greenLight) this.Name = name; this.greenLight = greenLight; public void GoLeft() Console.WriteLine(string.Format(1司机,0,请向左开车., greenLight.Notice, Name); 再创建Pedestrian.cs:?1234567891011121314public class Pedestrian private string Name; private PilotLamp greenLight; public Pedestrian(string name, PilotLamp greenLight) this.Name = name; this.greenLight = greenLight; public void GoThrough() Console.WriteLine( string.Format(0同志,1,请向前走., Name, greenLight.Notice); 最后再调用:?1234567891011121314151617181920212223242526272829303132public partial class Run : Form public Run() InitializeComponent(); private void btnRun_Click(object sender, EventArgs e) /- TrafficLight trafficLight = new TrafficLight(); Driver driverOne = new Driver(张三, trafficLight); Driver driverTwo = new Driver(李四, trafficLight); Pedestrian pedestrianOne = new Pedestrian(王五, trafficLight); Pedestrian pedestrianTwo = new Pedestrian(麻六, trafficLight); trafficLight.Notices += new Observer.EventHandler(driverOne.GoLeft); trafficLight.Notices += new Observer.EventHandler(driverTwo.GoLeft); trafficLight.Notices += new Observer.EventHandler(pedestrianOne.GoThrough); trafficLight.Notices += new Observer.EventHandler(pedestrianTwo.GoThrough); trafficLight.Notice = 绿灯亮了.; trafficLight.TurnOn(); /- 输出时选控制台应用程序如图:结果如下图:事件的使用示例:?123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263namespace DelegateAndEvent class Program static void Main(string args) Publishser pub = new Publishser(); OneScriber oneSub = new OneScriber(); TwoScriber twoSub = new TwoScriber(); ThreeScriber threeSub = new ThreeScriber (); pub.NumberChanged += new GeneralEventHandler(oneSub.OnNumberChanged); pub.NumberChanged += new GeneralEventHandler(twoSub.OnNumberChanged); pub.NumberChanged += new GeneralEventHandler(threeSub.OnNumberChanged); pub.DoSomething(); public delegate string GeneralEventHandler(); public class Publishser public event GeneralEventHandler NumberChanged; public void DoSomething() if (NumberChanged != null) Delegate generalEventHandlers = NumberChanged.GetInvocationList(); foreach (Delegate generalEventHandler in generalEventHandlers) GeneralEventHandler mothed = (GeneralEventHandler)generalEventHandler; string rtn = mothed(); Console.WriteLine(rtn); System.Threading.Thread.Sleep(2000); public class OneScriber public string OnNumberChanged() return One Subscriber; public class TwoScriber public string OnNumberChanged() return Two Subscriber; public class ThreeScriber public string OnNumberChanged() return Three Subscriber; 运行结果:注意到Delegate是GeneralEventHandler 的基类,所以为了触发事件,先要进行一个向下的强制转换,之后才能在其上触发事件,调用所有注册对象的方法。除了使用这种方式以外,还有一种更灵活方式可以调用方法,它是定义在Delegate基类中的DynamicInvoke()方法:public object DynamicInvoke(params object args);这可能是调用委托最通用的方法了,适用于所有类型的委托。它接受的参数为object,也就是说它可以将任意数量的任意类型作为参数,并返回单个object对象。上面的DoSomething()方法也可以改写成下面这种通用形式:代码作如下改动:?1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768namespace DelegateAndEvent class Program static void Main(string args) Publishser pub = new Publishser(); OneScriber oneSub = new OneScriber(); TwoScriber twoSub = new TwoScriber(); ThreeScriber threeSub = new ThreeScriber(); pub.NumberChanged += new GeneralEventHandler(oneSub.OnNumberChanged); pub.NumberChanged += new GeneralEventHandler(twoSub.OnNumberChanged); pub.NumberChanged += new GeneralEventHandler(threeSub.OnNumberChanged); List strlist = pub.DoSomething(); foreach (string result in strlist) Console.WriteLine(result); System.Threading.Thread.Sleep(5000); public delegate string GeneralEventHandler(); public class Publishser public event GeneralEventHandler NumberChanged; public List DoSomething() List strList = new List(); if (NumberChanged = null) return strList; Delegate generalEventHandlers = NumberChanged.GetInvocationList(); foreach (Delegate generalEventHandler in generalEventHandlers) / GeneralEventHandler mothed = (GeneralEventHandler)generalEventHandler; string rtn = generalEventHandler.DynamicInvoke(null).ToString(); strList.Add(rtn); return strList; public class OneScriber public string OnNumberChanged() return One Subscriber; public class TwoScriber public string OnNumberChanged() return Two Subscriber; public class ThreeScriber public string OnNumberChanged() return Three Subscriber; 结果如下:还是一样的结果.委托的定义会生成继承自MulticastDelegate的完整的类,其中包含Invoke()、BeginInvoke()和EndInvoke()方法。当我们直接调用委托时,实际上是调用了Invoke()方法,它会中断调用它的客户端,然后在客户端线程上执行所有订阅者的方法(客户端无法继续执行后面代码),最后将控制权返回客户端。注意到BeginInvoke()、EndInvoke()方法,在.Net中,异步执行的方法通常都会配对出现,并且以Begin和End作为方法的开头(最常见的可能就是Stream类的BeginRead()和EndRead()方法了)。它们用于方法的异步执行,即是在调用BeginInvoke()之后,客户端从线程池中抓取一个闲置线程,然后交由这个线程去执行订阅者的方法,而客户端线程则可以继续执行下面的代码。BeginInvoke()接受“动态”的参数个数和类型,为什么说“动态”的呢?因为它的参数是在编译时根据委托的定义动态生成的,其中前面参数的个数和类型与委托定义中接受的参数个数和类型相同,最后两个参数分别是AsyncCallback和Object类型,对于它们更具体的内容,可以参见下一节委托和方法的异步调用部分。现在,我们仅需要对这两个参数传入null就可以了。另外还需要注意几点: 在委托类型上调用BeginInvoke()时,此委托对象只能包含一个目标方法,所以对于多个订阅者注册的情况,必须使用GetInvocationList()获得所有委托对象,然后遍历它们,分别在其上调用BeginInvoke()方法。如果直接在委托上调用BeginInvoke(),会抛出异常,提示“委托只能包含一个目标方法”。 如果订阅者的方法抛出异常,.NET会捕捉到它,但是只有在调用EndInvoke()的时候,才会将异常重新抛出。而在本例中,我们不使用EndInvoke()(因为我们不关心订阅者的执行情况),所以我们无需处理异常,因为即使抛出异常,也是在另一个线程上,不会影响到客户端线程(客户端甚至不知道订阅者发生了异常,这有时是好事有时是坏事)。 BeginInvoke()方法属于委托定义所生成的类,它既不属于MulticastDelegate也不属于Delegate基类, 我们需要进行一个向下转换,来获取到实际的委托类型。示例:?123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869namespace DelegateAndEvent class Program static void Main(string args) Publishser pub = new Publishser(); OneScriber oneSub = new OneScriber(); TwoScriber twoSub = new TwoScriber(); ThreeScriber threeSub = new ThreeScriber(); pub.NumberChanged += new GeneralEventHandler(oneSub.OnNumberChanged); pub.NumberChanged += new GeneralEventHandler(twoSub.OnNumberChanged); pub.NumberChanged += new GeneralEventHandler(threeSub.OnNumberChanged); List strlist = pub.DoSomething(); foreach (string result in strlist) Console.WriteLine(result); System.Threading.Thread.Sleep(5000); public delegate string GeneralEventHandler(object sender,EventArgs e); public class Publishser public event GeneralEventHandler NumberChanged; public List DoSomething() List strList = new List(); if (NumberChanged = null) return strList; Delegate generalEventHandlers = NumberChanged.GetInvocationList

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论