委托和事件

上传人:小*** 文档编号:115811519 上传时间:2022-07-04 格式:PPT 页数:49 大小:341KB
收藏 版权申诉 举报 下载
委托和事件_第1页
第1页 / 共49页
委托和事件_第2页
第2页 / 共49页
委托和事件_第3页
第3页 / 共49页
资源描述:

《委托和事件》由会员分享,可在线阅读,更多相关《委托和事件(49页珍藏版)》请在装配图网上搜索。

1、第9章 常用基础类和反射 委托 系统提供的委托 事件 示例9.1 委托委托什么是委托?在现实生活中,委托就是让别人去代替自己办事。比如你有一份资料要发给客户,而自己不便亲自去做。这时可以派助手小王去给用户发传真,也可以让小王发电子邮件。处理的方法有很多种,小王只需要获得你的授权拿到资料就可以为你办事了。在C#中,可以把委托看做一个方法指针,该指针在不同时刻可以指向不同的方法,并且可以通过该委托执行这些方法。9.1 委托委托委托如何做到这点呢?在面向对象的程序设计中,委托是能够引用方法的对象。因此,创建委托时,创建的是能够存储方法引用的对象。怎样理解方法的引用?我们知道,引用的本质是内存地址,因

2、此,对象引用就是对象的地址。方法虽然不是对象,它同样在内存中有一个物理位置,而且其入口点就是调用方法时调用的地址。将此地址传给委托,一但委托引用一个方法,就能够通过该委托来调用该方法。9.1 委托委托例如,假设有两个方法MulFun和AddFun。MulFun接受两个整型参数,返回它们的乘积。AddFun接受两个整型参数,返回它们的和。既然两个方法的参数个数、类型和返回值类型都一样,就可以创建一个委托CalFunDelegate,使其在某个时刻指向MulFun,在某个时刻指向AddFun。如果该委托在指向MulFun时调用,则将对委托中传递的参数做乘法计算;如果该委托在指向AddFun时调用,

3、则将对委托中传递的参数做加法计算。9.1.1 定义委托定义委托定义委托的语法如下:AccessModifier delegate ReturnType DelegateName(parameter-list);其中,AccessModifier是访问修饰符,Delegate是委托关键字,ReturnType和parameter-list是委托所引用方法的返回值类型和参数列表。这里应注意,定义委托和定义方法非常类似,只是没有定义委托的实现,因此委托的语法以分号结束。9.1.1 定义委托定义委托委托定义的例子如下面的代码段所示:public delegate int CallFun(int a,i

4、nt b);此声明定义了一个名为CallFun的委托类型,它可以引用任何参数为两个int类型,并且返回值为int类型的方法9.1.2实例化委托实例化委托 因为委托是一个类型,定义委托后,必须实例化才能使用。实例化委托的过程就是将委托和方法或代码关联的过程。public delegate int CallFun(int a,int b);/声明一个委托类型class MathsOperationspublic int MulFun(int a,int b)return a*b;9.1.2实例化委托实例化委托 public int AddFun(int a,int b)return a+b;cla

5、ss Test static void Main()CallFun delegateObj;/声明该委托类型的变量MathsOperations math=new MathsOperations();/创建委托对象,同时指定要引用的方法 delegateObj=math.MulFun;9.1.2实例化委托实例化委托在上述代码中,首先定义了一个MathsOperations类,在该类中创建了两个实例方法MulFun和AddFun,注意它们的方法参数和返回值必须和委托类声明相同,否则委托将不能关联这两个方法。接下来在测试类Test的Main方法的第一行声明委托对象delegateObj,在下一行创

6、建类MathsOperations的一个对象math,最后,将委托对象(delegateObj)与方法MulFun关联起来。9.1.3 使用委托使用委托使用委托意味着使用委托来将信息发送给关联的方法。使用委托的方式与方法调用相同,唯一的区别在于没有调用委托的实现(该实现并不存在),而是调用与该委托关联的方法的代码。委托对象可以有参数,这个参数并没有被委托使用,而是传递给关联的方法,由关联的方法进行处理并将结果传给委托。在上面的示例中,如果调用委托实际是调用MulFun方法,int t=delegateObj(5,3);Console.WriteLine(“结果是0”,t);结果是15。委托不仅

7、可以关联实例方法,也可以关联静态方法。9.1.3 使用委托使用委托delegate void Del();class SampleClass public void InstanceMethod()/实例方法 System.Console.WriteLine(A message from the instance method.);static public void StaticMethod()/静态方法 System.Console.WriteLine(A message from the static method.);9.1.3 使用委托使用委托class TestSampleClas

8、s static void Main()SampleClass sc=new SampleClass();Del d=sc.InstanceMethod;/关联到实例方法 d();d=SampleClass.StaticMethod;/关联到静态方法 d();运行结果如图9.1.3 使用委托使用委托9.2 匿名方法匿名方法在创建新方法的系统开销不必要时,允许对委托进行实例化,并立即指定委托将在被调用时处理的代码块。这被称为匿名方法。要将代码块传递为委托参数,创建匿名方法则是唯一的方法。例如:/创建一个委托 delegate void Del(int x);/实例化委托,指定该委托要执行的代码块

9、 Del d=delegate(int k)/*.*/;0下面的示例演示了实例化委托的两种方法:其一是使委托与匿名方法关联,其二是使委托与命名方法(DoWork)关联。两种方法都会在调用委托时显示一条消息。delegate void Printer(string s);/声明一个委托类型class TestClassstatic void Main()/采用匿名方法Printer p=delegate(string j)System.Console.WriteLine(j);/匿名方法代码块;/调用委托执行这段代码p(The delegate using the anonymous metho

10、d is called.);/采用命名方法p=TestClass.DoWork;/调用委托执行这个方法p(The delegate using the named method is called.);/命名方法static void DoWork(string k)System.Console.WriteLine(k);使用匿名方法,不必创建单独的方法,因此减少了实例化委托所需的编码系统开销。9.3 事事 件件什么是事件呢?委托和事件有什么区别呢?我们还是用例子来说明问题。在讲委托的时候,我们假设员工小王是你的委托人,专门负责为你发送资料给客户。当然他可以采用多种措施来完成这个任务,比如发邮

11、件或传真。现在我们知道,这些措施都是命名或匿名方法,必须在使用委托之前和委托关联。在上面这个场景中,我们可以采取另外的方式完成你的任务。比如,你可以事先对你的所有员工说,“当我有要求时随时为我服务”,这样,一旦你需要发送资料,你的所有员工都会得到这个消息并为你服务。当然,如果不是你的员工,他是不会为你做任何事的。9.3 事事 件件让我们从程序员的角度来看这个场景。当你向外宣布需要发送资料时,事件发生了,你就是事件发布者,向外发布一个事件。你的所有员工为事件的订户。他们依据事件进行相应的处理,如发邮件或传真,这个过程叫事件处理程序。同时,不是你的下属则根本不关心事件发布者的话,因为他们没有预订这

12、个事件。9.3 事事 件件C#中的事件处理采用了同样的方式。我们把事件信息通知给其他对象的对象称为发布方,注册到事件的对象称为订户,事件即为事件发布方向外界发出的通知,而事件处理程序即使事件订户应事件发生而做出的反应(具体到程序上,就是针对该事件而写的那些处理代码)。这里需要注意的是,订户必须预订该事件后,位于订户对象内的事件处理程序才可能会被执行。孤立的事件处理程序不会被执行。9.3 事事 件件 总的说来,C#的事件具有如下特点:事件是基于委托的。发行者确定何时引发事件,订户确定执行何种操作来响应该事件。一个事件可以有多个订户。一个订户可处理来自多个发行者的多个事件。没有订户的事件永远不会被

13、调用。事件通常用于通知用户操作(如:图形用户界面中的按钮单击或菜单选择操作)。如果一个事件有多个订户,当引发该事件时,会同步调用多个事件处理程序。在.NET Framework类库中,事件是基于 Event Handler 委托和 EventArgs 基类的。9.3.1 定义事件 C#中处理事件的步骤如下:(1)定义事件。(2)订阅事件。(3)引发事件。9.3.1 定义事件 C#中的事件需要借助于委托。它们使用委托调用预订了该事件的对象中的方法。当发布方引发事件时,可能会调用许多委托,一般而言,有多少订户,就应调用多少委托。定义事件之前,需由发布方先定义委托,事件取决于委托。9.3.1 定义事

14、件事件定义的语法如下:public event xxxEventHandler EventName;其中,event是事件的关键字;xxxEventHandler表示事件使用的委托类型,常采用“EventHandler”的命名习惯;EventName是事件的名称。下面的代码段演示了这一过程。public delegate void AskEventHandler();public event AskEventHandler AskEvent;这段代码执行了以下功能:(1)首先创建一个作为事件基础的委托类型AskEventHandler。(2)然后创建基于该委托的事件AskEvent。9.3.2

15、 预订事件预订事件就是将发布方的事件和订户的事件处理程序进行绑定的过程,这样,当事件被引发时,将会执行响应的绑定代码。能否预订事件取决于事件是否存在。如果事件存在,那么要给对象预订事件,只须使用加法赋值运算符(+=)绑定一个当该事件被引发时将调用方法的委托即可,要取消预订,可以采用减法赋值运算符(-=)。如果事件AskEvent存在,若要让对象预订该事件,只需编写如代码段所示的语句。ObjM.AskEvent+=new AskEventHandler(objA.Method);ObjM.AskEvent+=new AskEventHandler(objB.Method);9.3.2 预订事件

16、在上述代码段中,订户objA和订户objB分别预订了事件发布者对象ObjM的事件AskEvent,并规定了当此事件引发时,将分别调用各自的Method方法来进行响应。在.NET2.0版本中,预订事件有了更为简明的方式,下面这段代码是可行的。ObjM.AskEvent+=objA.Method;此外,我们还可以为预订事件使用匿名方法。在下面的示例中,假设名为 ObjM的对象拥有一个名为AskEvent的事件,该事件的委托类型为无返回值无参数的形式,此时可直接采用关键字delegate来使该事件和代码进行绑定。请注意,订户类需要引用ObjM才能订阅其事件。ObjM.AskEvent+=delega

17、te()/事件处理的代码;请注意,如果你是使用匿名方法订阅的事件,该事件的取消订阅过程就比较麻烦。所以一般不建议使用匿名方法预定事件9.3.3 通知预订事件通知预订事件要通知所有已预订了事件的对象,只需引发事件。引发事件与调用方法相似。请研究下面的代码段。if(condition)AskEvent();上面的代码段对一个特定的条件进行检查,如果该条件的值为true,则引发事件AskEvent。请注意,引发事件的语法与调用方法的语法完全相同。引发AskEvent时,将调用所有预订了该特定事件的对象的委托,去执行相应的事件处理程序。如果不存在预订事件的对象,却引发了事件,则产生异常。考虑到本节开始

18、时提出的事件场景,结合使用事件的三个步骤,我们给出了下面使用事件的完整示例。public class Master /发布方类 public delegate void AskEventHandler();/声明委托 public event AskEventHandler AskEvent;/声明事件 public void Ask()Console.WriteLine(请帮我把资料发送给客户);AskEvent();/引发事件public static void Main()Master me=new Master();Employee x1=new Employee(小王);Employ

19、ee x2=new Employee(小李);me.AskEvent+=new AskEventHandler(x1.DoWorkByTele);/预订事件 me.AskEvent+=new skEventHandler(x2.DoWorkByEmail);me.Ask();public class Employee/订户类 private string name;public Employee(string n)name=n;public void DoWorkByTele()/事件处理程序1 Console.WriteLine(0将资料传真给用户,name);public void DoW

20、orkByEmail()/事件处理程序2 Console.WriteLine(0将资料Email给用户,name);9.3.3 示例示例在上面的示例中共创建了两个类,即发布方类Master和订户类Employee。在Master类中,定义了AskEventHandler委托和AskEvent事件,还定义了引发事件的Ask方法。在Employee类中,定义了两个不同的事件处理方法,虽然它们的实现不同,但方法的签名必须和委托声明保持一致。最后,在Master类的Main方法中,创建了三个对象,并将两个订户类对象的方法注册到发布类对象的事件上。程序最后调用Ask方法,从而引发了这个事件。程序运行的最

21、终结果如图所示。9.3.3 示例示例综合案例:处理综合案例:处理SARS紧急事件紧急事件 在本章前面的几节中,我们主要介绍了集合、异常、属性、索引、委托和事件等C#的高级概念。在本节中,将使用前面的部分知识,带领大家创建一个使用委托和事件的综合案例。分析:本案例的场景如图4-16所示。本案例将创建四个类,包括政府类,医院类、疾控中心类、病人类。通过构建这些类将模拟一个SARS病人通知和处理流程。当政府发现SARS病人后,将向外发布事件,以启动紧急事件处理机制。医院和疾控中心收到事件指令后,将执行相应的处理机制:医院将该病人隔离起来,而疾控中心则负责向市民预警。图4-16 SARS疾病处理机制

22、实现:首先来看病人类SarsPerson的创建。/定义SARS病人类 public class SarsPerson private string s_name;/定义SARS病人的姓名域public string Name /定义SARS病人的姓名属性 get return s_name;set s_name=value;private string m_sex;/定义SARS病人的性别域public string Sex /定义SARS病人的性别属性 getreturn m_sex;set if(value!=男)&(value!=女)throw new Exception(输入错误:性别

23、必须为男/女!);else m_sex=value;private int m_age;/定义SARS病人的年龄域 public int Age/定义SARS病人的年龄属性 getreturn m_age;set if(value150)throw new Exception(输入错误:年龄范围必须在0-150之间!);else m_age=value;public string m_address;/定义SARS病人的地址域 说明:在上面的代码段中,首先为SarsPerson类定义了姓名、性别、年龄、地址等域变量,并为其封装了属性。这里注意,在性别和年龄的属性设置中,对一些错误输入进行了判断

24、。一旦用户输入的信息不正确,则抛出相应的异常。接下来看医院类ClassHospital和疾控中心类ClassCDC的创建。/定义根据通知执行治疗的医院类 class ClassHospital public void CureMethod()/事件执行程序1 Console.WriteLine(处理一:把该SARS病人隔离起来治疗!);/定义根据通知执行监测的疾病控制中心类 class ClassCDC ublic void ReportMethod()/事件执行程序2 Console.WriteLine(处理二:把发生 SARS紧急事件 Notify市民!);说明:这两个类的定义比较简单,每

25、个类中只有一个方法。这个方法就是当订户类接收事件时所处理的操作步骤。政府类Government的创建代码如下所示:/定义政府类 class Government public delegate void WhoDelegate();/创建事件委托public event WhoDelegate Urgency;/定义基于委托类型的紧急事件 public void Notify(SarsPerson illness)/引发事件的方法if(Urgency!=null)Console.WriteLine(发生紧急事件:出现非典疑似病例:);/隐式调用属性的get访问器 Console.WriteLi

26、ne(*:);Console.WriteLine(0,1,2,3,illness.Name,illness.Sex,illness.Age,illness.m_address);Console.WriteLine(*:);Console.WriteLine(启动应急事件处理机制:);Urgency();/激活事件,执行应急事件对应的处理机制 public static void Main()/主函数 说明:从上面的代码可以看出,Government类相当于事件的发布方。该类首先定义了一个委托和基于该委托的事件。接下来,在Notify方法中接受一个SarsPerson对象作为参数,输出该对象的详

27、细信息并引发事件。此外,在该类中还有一个主函数Main(),它的具体代码如下所示:public static void Main()tryGovernment dA=new Government();ClassHospital objA=new ClassHospital();ClassCDC objB=new ClassCDC();/预订事件:把事件处理程序和事件关联起来dA.Urgency+=new Government.WhoDelegate(objA.CureMethod);dA.Urgency+=new Government.WhoDelegate(objB.ReportMethod

28、);/出现SRAS病人illnessSarsPerson illness=new SarsPerson();/收集该病人的信息Console.WriteLine(t请输入SARS病人的信息:);Console.WriteLine(*:);Console.WriteLine(请输入SARS病人的姓名:);illness.Name=Console.ReadLine();Console.WriteLine(请输入SARS病人的性别:);illness.Sex=Console.ReadLine();/输入性别Console.WriteLine(请输入SARS病人的年龄:);illness.Age =i

29、nt.Parse(Console.ReadLine();/输入年龄 Console.WriteLine(请输入SARS病人的地址:);illness.m_address=Console.ReadLine();/输入地址 Console.WriteLine(*:);/通知订户,事件发生 dA.Notify(illness);catch(Exception e)Console.WriteLine(e.Message);说明:在主函数中,首先创建了Government、ClassHospital、ClassCDC三个对象,并为发布方类Government的事件Urgency预订了事件处理程序。接下来

30、创建一个SarsPerson对象,由控制台对其成员属性赋值。最后,调用Government对象中的Notify方法,从而引发Urgency事件。此时将执行相应的事件处理程序。程序运行结果如图所示。图4-17 综合案例运行结果 练习:研究下列代码,理解委托和事件的概念。在该示例中,创建了名为Saywish的委托,它接受字符串类型的参数。然后创建了称为Wish的类,该类定义了称为OnWish的事件。程序中将显示祝愿消息“祝你生日快乐”,并显示使用bday_wish()方法所接受的人的姓名。实现步骤:(1)创建一个名为“lesson4 lab2”的控制台应用程序。(2)将Program.cs更名为E

31、ventDemo.cs。(3)将以下代码添加到“EventDemo.cs”中。using System;namespace Test public delegate void Saywish(string strName);public class Wish/发布者类 public event Saywish OnWish;/创建一个事件 public void bday_wish()/引发事件的方法 string strName;Console.WriteLine(请输入你的姓名:);strName=Console.ReadLine();OnWish(strName);class Saywi

32、shHanler/订户类 public void ShowWish(string strName)/事件处理程序 Console.WriteLine(strName+,+祝你生日快乐!);class Demodele public static void Main()Wish mywish=new Wish();SaywishHanler mySayWish=new SaywishHanler();mywish.OnWish+=new Saywish(mySayWish.ShowWish);/预订事件 mywish.bday_wish();(4)选择“生成”“生成解决方案”选项,以生成此项目。(5)选择“调试”“开始执行(不调试)”选项来执行此应用程序。(6)运行结果如图4-18所示。图4-18 运行结果 第二部分:练习第二部分:练习 练习3:修改练习1,创建能捕获所有异常的语句,并能根据当前的异常显示相关的异常信息。第三部分:作业第三部分:作业 练习4:编写程序,创建委托OddNumberFinder,用于显示1100范围内的奇数。创建事件OnOddNumberEvent,每次遇到奇数时就引发该事件,并且该事件应显示所遇到的奇数的值。

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

copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!