ImageVerifierCode 换一换
格式:DOCX , 页数:26 ,大小:29.56KB ,
资源ID:26026292      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/26026292.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(C#中的委托和事件续.docx)为本站会员(b****9)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

C#中的委托和事件续.docx

1、C#中的委托和事件续引言如果你看过了 C#中的委托和事件 一文,我想你对委托和事件已经有了一个基本的认识。但那些远不是委托和事件的全部内容,还有很多的地方没有涉及。本文将讨论委托和事件一些更为细节的问题,包括一些大家常问到的问题,以及事件访问器、异常处理、超时处理和异步方法调用等内容。为什么要使用事件而不是委托变量?在 C#中的委托和事件 中,我提出了两个为什么在类型中使用事件向外部提供方法注册,而不是直接使用委托变量的原因。主要是从封装性和易用性上去考虑,但是还漏掉了一点,事件应该由事件发布者触发,而不应该由客户端(客户程序)来触发。这句话是什么意思呢?请看下面的范例:NOTE:注意这里术语

2、的变化,当我们单独谈论事件,我们说发布者(publisher)、订阅者 (subscriber)、客户端(client)。当我们讨论Observer模式,我们说主题(subject)和观察者(observer)。客户 端通常是包含Main()方法的Program类。class Program static void Main(string args) Publishser pub = new Publishser(); Subscriber sub = new Subscriber(); pub.NumberChanged += new NumberChangedEventHandler(su

3、b.OnNumberChanged); pub.DoSomething(); / 应该通过DoSomething()来触发事件 pub.NumberChanged(100); / 但可以被这样直接调用,对委托变量的不恰当使用 / 定义委托public delegate void NumberChangedEventHandler(int count);/ 定义事件发布者public class Publishser private int count; public NumberChangedEventHandler NumberChanged; / 声明委托变量 /public event

4、NumberChangedEventHandler NumberChanged; / 声明一个事件 public void DoSomething() / 在这里完成一些工作 . if (NumberChanged != null) / 触发事件 count+; NumberChanged(count); / 定义事件订阅者public class Subscriber public void OnNumberChanged(int count) Console.WriteLine(Subscriber notified: count = 0, count); 上面代码定义了一个NumberC

5、hangedEventHandler委托,然后我们创建了事件的发布者Publisher和订阅者 Subscriber。当使用委托变量时,客户端可以直接通过委托变量触发事件,也就是直接调用pub.NumberChanged(100),这将会影 响到所有注册了该委托的订阅者。而事件的本意应该为在事件发布者在其本身的某个行为中触发,比如说在方法DoSomething()中满足某个条件后触发。通过添加event关键字来发布事件,事件发布者的封装性会更好,事 件仅仅是供其他类型订阅,而客户端不能直接触发事件(语句pub.NumberChanged(100)无法通过编译),事件只能在事件发布者 Publi

6、sher类的内部触发(比如在方法pub.DoSomething()中),换言之,就是NumberChanged(100)语句只能在 Publisher内部被调用。大家可以尝试一下,将委托变量的声明那行代码注释掉,然后取消下面事件声明的注释。此时程序是无法编译的,当你使用了event关键字之后,直接在 客户端触发事件这种行为,也就是直接调用pub.NumberChanged(100),是被禁止的。事件只能通过调用DoSomething()来触 发。这样才是事件的本意,事件发布者的封装才会更好。就好像如果我们要定义一个数字类型,我们会使用int而不是使用object一样,给予对象过多的能力并不见得

7、是一件好事,应该是越合适越好。尽管 直接使用委托变量通常不会有什么问题,但它给了客户端不应具有的能力,而使用事件,可以限制这一能力,更精确地对类型进行封装。NOTE:这里还有一个约定俗称的规定,就是订阅事件的方法的命名,通常为“On事件名”,比如这里的OnNumberChanged。为什么委托定义的返回值通常都为void?尽管并非必需,但是我们发现很多的委托定义返回值都为void,为什么呢?这是因为委托变量可以供多个订阅者注册,如果定义了返回值,那么多个订阅 者的方法都会向发布者返回数值,结果就是后面一个返回的方法值将前面的返回值覆盖掉了,因此,实际上只能获得最后一个方法调用的返回值。可以运行

8、下面的代 码测试一下。除此以外,发布者和订阅者是松耦合的,发布者根本不关心谁订阅了它的事件、为什么要订阅,更别说订阅者的返回值了,所以返回订阅者的方法返回 值大多数情况下根本没有必要。class Program static void Main(string args) Publishser pub = new Publishser(); Subscriber1 sub1 = new Subscriber1(); Subscriber2 sub2 = new Subscriber2(); Subscriber3 sub3 = new Subscriber3(); pub.NumberChang

9、ed += new GeneralEventHandler(sub1.OnNumberChanged); pub.NumberChanged += new GeneralEventHandler(sub2.OnNumberChanged); pub.NumberChanged += new GeneralEventHandler(sub3.OnNumberChanged); pub.DoSomething(); / 触发事件 / 定义委托public delegate string GeneralEventHandler();/ 定义事件发布者public class Publishser p

10、ublic event GeneralEventHandler NumberChanged; / 声明一个事件 public void DoSomething() if (NumberChanged != null) / 触发事件 string rtn = NumberChanged(); Console.WriteLine(rtn); / 打印返回的字符串,输出为Subscriber3 / 定义事件订阅者public class Subscriber1 public string OnNumberChanged() return Subscriber1; public class Subsc

11、riber2 /* 略,与上类似,返回Subscriber2*/ public class Subscriber3 /* 略,与上类似,返回Subscriber3*/ 如果运行这段代码,得到的输出是Subscriber3,可以看到,只得到了最后一个注册方法的返回值。如何让事件只允许一个客户订阅?少数情况下,比如像上面,为了避免发生“值覆盖”的情况(更多是在异步调用方法时,后面会讨论),我们可能想限制只允许一个客户端注册。此时怎么做呢?我们可以向下面这样,将事件声明为private的,然后提供两个方法来进行注册和取消注册:/ 定义事件发布者public class Publishser priv

12、ate event GeneralEventHandler NumberChanged; / 声明一个私有事件 / 注册事件 public void Register(GeneralEventHandler method) NumberChanged = method; / 取消注册 public void UnRegister(GeneralEventHandler method) NumberChanged -= method; public void DoSomething() / 做某些其余的事情 if (NumberChanged != null) / 触发事件 string rtn

13、 = NumberChanged(); Console.WriteLine(Return: 0, rtn); / 打印返回的字符串,输出为Subscriber3 NOTE:注意上面,在UnRegister()中,没有进行任何判断就使用了NumberChanged-=method语句。这是因为即使method方法没有进行过注册,此行语句也不会有任何问题,不会抛出异常,仅仅是不会产生任何效果而已。注意在Register()方法中,我们使用了赋值操作符“=”,而非“+=”,通过这种方式就避免了多个方法注册。上面的代码尽管可以完成我们的需要,但是此时大家还应该注意下面两点:1、将NumberChang

14、ed声明为委托变量还是事件都无所谓了,因为它是私有的,即便将它声明为一个委托变量,客户端也看不到它,也就无法通 过它来触发事件、调用订阅者的方法。而只能通过Register()和UnRegister()方法来注册和取消注册,通过调用 DoSomething()方法触发事件(而不是NumberChanged本身,这在前面已经讨论过了)。2、我们还应该发现,这里采用的、对NumberChanged委托变量的访问模式和C#中的属性是多么类似啊?大家知道,在C#中通常一个属性对 应一个类型成员,而在类型的外部对成员的操作全部通过属性来完成。尽管这里对委托变量的处理是类似的效果,但却使用了两个方法来进行

15、模拟,有没有办法像使 用属性一样来完成上面的例子呢?答案是有的,C#中提供了一种叫事件访问器(Event Accessor)的东西,它用来封装委托变量。如下面例子所示:class Program static void Main(string args) Publishser pub = new Publishser(); Subscriber1 sub1 = new Subscriber1(); Subscriber2 sub2 = new Subscriber2(); pub.NumberChanged -= sub1.OnNumberChanged; / 不会有任何反应 pub.Numb

16、erChanged += sub2.OnNumberChanged; / 注册了sub2 pub.NumberChanged += sub1.OnNumberChanged; / sub1将sub2的覆盖掉了 pub.DoSomething(); / 触发事件 / 定义委托public delegate string GeneralEventHandler();/ 定义事件发布者public class Publishser / 声明一个委托变量 private GeneralEventHandler numberChanged; / 事件访问器的定义 public event General

17、EventHandler NumberChanged add numberChanged = value; remove numberChanged -= value; public void DoSomething() / 做某些其他的事情 if (numberChanged != null) / 通过委托变量触发事件 string rtn = numberChanged(); Console.WriteLine(Return: 0, rtn); / 打印返回的字符串 / 定义事件订阅者public class Subscriber1 public string OnNumberChange

18、d() Console.WriteLine(Subscriber1 Invoked!); return Subscriber1; public class Subscriber2 /* 与上类同,略 */public class Subscriber3 /* 与上类同,略 */上面代码中类似属性的public event GeneralEventHandler NumberChanged add.remove.语句便是事件访问器。使用了事件访问器以后,在DoSomething方法中便只能通过 numberChanged委托变量来触发事件,而不能NumberChanged事件访问器(注意它们的大

19、小写不同)触发,它只用于注册和取消注册。下 面是代码输出:Subscriber1 Invoked!Return: Subscriber1获得多个返回值与异常处理现在假设我们想要获得多个订阅者的返回值,以List的形式返回,该如何做呢?我们应该记得委托定义在编译时会生成一 个继承自MulticastDelegate的类,而这个MulticastDelegate又继承自Delegate,在Delegate内部,维护了 一个委托链表,链表上的每一个元素,为一个只包含一个目标方法的委托对象。而通过Delegate基类的GetInvocationList()静态方 法,可以获得这个委托链表。随后我们遍历

20、这个链表,通过链表中的每个委托对象来调用方法,这样就可以分别获得每个方法的返回值:class Program4 static void Main(string args) Publishser pub = new Publishser(); Subscriber1 sub1 = new Subscriber1(); Subscriber2 sub2 = new Subscriber2(); Subscriber3 sub3 = new Subscriber3(); pub.NumberChanged += new DemoEventHandler(sub1.OnNumberChanged);

21、pub.NumberChanged += new DemoEventHandler(sub2.OnNumberChanged); pub.NumberChanged += new DemoEventHandler(sub3.OnNumberChanged); List list = pub.DoSomething(); /调用方法,在方法内触发事件 foreach (string str in list) Console.WriteLine(str); public delegate string DemoEventHandler(int num);/ 定义事件发布者public class

22、Publishser public event DemoEventHandler NumberChanged; / 声明一个事件 public List DoSomething() / 做某些其他的事 List strList = new List(); if (NumberChanged = null) return strList; / 获得委托数组 Delegate delArray = NumberChanged.GetInvocationList(); foreach (Delegate del in delArray) / 进行一个向下转换 DemoEventHandler met

23、hod = (DemoEventHandler)del; strList.Add(method(100); / 调用方法并获取返回值 return strList; / 定义事件订阅者public class Subscriber1 public string OnNumberChanged(int num) Console.WriteLine(Subscriber1 invoked, number:0, num); return Subscriber1 returned; public class Subscriber3 与上面类同,略public class Subscriber3 与上面

24、类同,略如果运行上面的代码,可以得到这样的输出:Subscriber1 invoked, number:100Subscriber2 invoked, number:100Subscriber3 invoked, number:100Subscriber1 returnedSubscriber2 returnedSubscriber3 returned可见我们获得了三个方法的返回值。而我们前面说过,很多情况下委托的定义都不包含返回值,所以上面介绍的方法似乎没有什么实际意义。其实通过这种方 式来触发事件最常见的情况应该是在异常处理中,因为很有可能在触发事件时,订阅者的方法会抛出异常,而这一异常会

25、直接影响到发布者,使得发布者程序中止, 而后面订阅者的方法将不会被执行。因此我们需要加上异常处理,考虑下面一段程序:class Program5 static void Main(string args) Publisher pub = new Publisher(); Subscriber1 sub1 = new Subscriber1(); Subscriber2 sub2 = new Subscriber2(); Subscriber3 sub3 = new Subscriber3(); pub.NumberChanged += new DemoEventHandler(sub1.OnN

26、umberChanged); pub.NumberChanged += new DemoEventHandler(sub2.OnNumberChanged); pub.NumberChanged += new DemoEventHandler(sub3.OnNumberChanged); public class Publisher public event EventHandler MyEvent; public void DoSomething() / 做某些其他的事情 if (MyEvent != null) try MyEvent(this, EventArgs.Empty); cat

27、ch (Exception e) Console.WriteLine(Exception: 0, e.Message); public class Subscriber1 public void OnEvent(object sender, EventArgs e) Console.WriteLine(Subscriber1 Invoked!); public class Subscriber2 public void OnEvent(object sender, EventArgs e) throw new Exception(Subscriber2 Failed); public class Subscriber3 /* 与Subsciber1类同,略*/注意到我

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1