




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第9章方法类型化——委托9.1委托9.2事件9.3小结9.1委托在C、C++和Pascal中,如果把函数的指针(地址)作为参数传递给另一个函数,当该指针被用于调用它所指向的函数时,我们就可以称之为回调函数。回调函数是一种功能强大的编程特性,窗口过程、异步过程调用等都需要使用回调函数。但是,函数指针只是一个内存地址,该地址不带任何额外信息,例如函数期望收到的参数个数、参数类型、函数的返回值类型以及函数的调用约定等,所以函数指针是非类型安全的。因此,为了保证程序的安全性,Java不提供任何具有指针函数功能的结构,但是C#提供这种结构,这就是类型安全的委托。9.1.1委托概述在C#中,委托是一种特殊的对象类型(即一种特殊的类),其特殊之处在于之前定义的对象类型都可以包含数据,而委托包含的只是方法的地址。也可以说,委托是对一类方法(参数和返回值类型相似的方法)的类型。和类在使用前要进行实例化一样,委托在使用前也要进行实例化。但需要注意的是,在术语方面,类有两个不同的术语,“类”表示较广义的定义,“对象”表示类的实例。但委托只有一个术语,在创建委托的实例时,所创建的委托的实例也称为委托。9.1.1委托概述(1)确定将要引用方法的签名,声明一个委托类型。一般语法形式如下:[访问修饰符]delegate返回值类型委托类名(形参列表);例如下列代码:publicdelegatevoidDel(stringmessage);委托声明在形式上类似于方法的定义,但是没有方法体,在返回值类型前需要添加delegate关键字。委托声明中的返回值类型、形参类型和形参个数要与被引用的方法匹配(不必完全匹配,委托支持协变与逆变,后面章节将进行详细介绍)。委托的声明实际就是一个类的定义(该类实际定义的是一类方法,而且只有方法,没有数据),所以可以在定义类的任何位置定义委托。9.1.1委托概述(2)实例化一个委托。一般语法形式如下:委托类名委托名=new委托类名(被引用的方法名);或者如下形式:委托类名委托名=被引用的方法名;例如下列代码。Delhandler=newDel(DelegateMethod);
或者如下形式。Delhandler=DelegateMethod;第二种语法是第一种语法的缩写,是C#2.0以上版本才支持的语法形式。因为C#2.0支持委托推断,即允许直接为委托实例指派方法名,而不需要先使用委托对象对其进行包装。示例代码中的DelegateMehod是一个被引用的具体方法名,不带“()”。因为handler实际是一个对象,对其进行赋值只是将DelegateMethod的内存单元地址传入handler中。有关委托的本质可参见后续章节。9.1.1委托概述(2)实例化一个委托。一般语法形式如下:委托类名委托名=new委托类名(被引用的方法名);或者如下形式:委托类名委托名=被引用的方法名;例如下列代码。Delhandler=newDel(DelegateMethod);
或者如下形式。Delhandler=DelegateMethod;第二种语法是第一种语法的缩写,是C#2.0以上版本才支持的语法形式。因为C#2.0支持委托推断,即允许直接为委托实例指派方法名,而不需要先使用委托对象对其进行包装。示例代码中的DelegateMehod是一个被引用的具体方法名,不带“()”。因为handler实际是一个对象,对其进行赋值只是将DelegateMethod的内存单元地址传入handler中。有关委托的本质可参见后续章节。9.1.1委托概述(3)委托调用。委托调用的目的就是调用委托所封装的方法。委托最主要的用途是实现函数回调.【例9-1】委托实现函数回调
publicdelegatevoidDel(stringmessage);classProgram{publicstaticvoidDelegateMethod(stringmessage){System.Console.WriteLine(message);}staticvoidMain(string[]args){
Delhandler=DelegateMethod;
handler("HelloWorld");//传入参数"HelloWorld"}}9.1.1委托概述【例9-2】委托实现函数传递
publicdelegatevoidDel(stringmessage);classProgram{
publicstaticvoidDelegateMethod(stringmessage){System.Console.WriteLine(message);}publicstaticvoidMethodWithCallback(intparam1,intparam2,Delcallback)//Del类委托做参数
{callback("Thenumberis:"+(param1+param2).ToString());}staticvoidMain(string[]args){
Delhandler=DelegateMethod;MethodWithCallback(1,2,handler);//委托实现函数传递
}9.1.1委托概述【例9-3】委托引用对象方法
publicdelegatevoidDel(stringmessage);publicclassMethodClass{publicvoidMethod(stringmessage) //非静态方法Method{System.Console.WriteLine(message);}}
9.1.1委托概述classProgram{
publicstaticvoidMethodWithCallback(intparam1,intparam2,Delcallback){
callback("Thenumberis:"+(param1+param2).ToString());}staticvoidMain(string[]args){MethodClassobj=newMethodClass();//MethodClass类的实例obj
Delpoint=obj.Method;//引用方法时一定要加上对象名
point("HelloWorld");
MethodWithCallback(1,2,point);}}9.1.3匿名方法在2.0之前的C#版本中,声明委托的惟一方法就是使用命名方法,即要使用委托传递和回调一个方法,该方法必须是已经存在的。C#2.0引入了匿名方法,即允许委托可以只传递代码块,使代码块成为委托参数。【例9-4】使用匿名方法。classprogram{delegatevoidMessage();delegateintAddOne(intv);staticvoidMain(string[]args){inty=10;AddOneao=delegate(intval){val++;returnval;};
9.1.3匿名方法Console.WriteLine("y={0},y++={1}",y,ao(y));Messagems=delegate(){Console.WriteLine("AnonymousMethod");};ms();Console.ReadLine();}}9.1.3匿名方法匿名方法可以带参数,也可以不带参数。当定义带有参数的匿名方法时,应在关键字delegage后面定义参数类型和名称。如同一个常规方法,方法签名必须与它指派的委托的定义相匹配。当调用委托时,可以传递参数的值,与正常的委托调用完全相同。当定义不带参数的匿名方法时,可以在关键字deletgate后面添加一对空括号。如果委托类型的返回值类型为void,则匿名方法不能有返回值;否则,匿名方法的返回值必须和委托类型的返回值兼容。对于上述示例代码中定义的两个匿名方法,返回值的类型分别为void和string。与普通的方法定义不同,匿名方法定义结束时,不能省略“;”。9.1.3匿名方法编译器生成的匿名方法在类中是private可见性的,这会禁止未在类型内部定义的任何访问该方法。因为匿名方法是私有的,所以匿名方法中不能使用跳转语句(goto、break或者continue)从代码块内跳转到代码块外部。同样,代码块外部的跳转语句也不能跳转到代码块内部。匿名方法内部不能访问不安全的代码。不能访问在匿名方法外部的ref参数和out参数,但是可以使用匿名方法访问外部定义的其他变量。is运算符的左侧不允许使用匿名方法。9.1.4创建多播委托前面介绍的委托,包括匿名方法,都是单链的,即一个委托对象只能对应一个回调方法。若要通过委托同时调用多个方法,即使这些方法的签名都相同,也需要分别定义对应的委托对象,然后显式调用这些委托对象。事实上,C#提供了更简捷的方式实现多个方法的同时调用,这种委托就是多播委托(也称作多路委托、多路广播或者委托链)。我们可以使用加法运算符(+)或者加法赋值运算符(+=)向委托的方法列表中添加额外的方法,也可以使用减法运算符(-)或者减法赋值运算符(-=)从委托的方法链表中减少方法。【例9-5】多播委托
classMyClass{publicvoidWordHello(strings){System.Console.WriteLine("Hello,{0}",s);}publicvoidWordGoodmorning(strings){System.Console.WriteLine("GoodMorning,{0}",s);}publicvoidWordGoodafternoon(strings){System.Console.WriteLine("Goodafternoon,{0}!",s);}}9.1.4创建多播委托classTest{publicdelegatevoidMyDelegate(strings);staticvoidMain(String[]args){MyClassMyObj=newMyClass();MyDelegatea,b,c,d,e;//定义了5个MyDelegate类型的委托对象
a=MyObj.WordHello;b=MyObj.WordGoodmorning;c=MyObj.WordGoodafternoon;d=a+b; //将a、b合并成dc+=d; //将d、c合并成新的cd("A");c("B");e=c-a; //将a从c中删除,e将只包含b和de("C");Console.ReadLine();}}9.1.4创建多播委托关于多播委托,需要注意如下事项。多播委托的签名需要统一返回void类型,否则只能得到委托调用的最后一个方法的结果。多播委托的调用方法链顺序并未被正式定义,因此需要避免编写依赖于特定顺序调用方法的代码。多播委托包含一个逐个调用的委托集合,如果调用的方法抛出了异常,整个迭代就会停止。9.1.4创建多播委托9.1.6手工迭代前面已经介绍了多播委托的创建和原理。通过创建委托链,能够实现对多个方法的调用。因为委托类型中的Invoke方法可以实现对_invocationList指向的数组的遍历。但是,通过分析Invoke的伪代码可以发现,它使用的是一个非常简单的算法,尽管该算法足以应付大部分情况,但是它也有很大的局限性,表现在如下两个方面。只能支持void返回值类型签名,否则只能返回最后一个被调用方法的返回值。如果某个被调用的方法抛出异常,后面的其他方法就不会再被调用。显然,这两点便显现出了多播委托的不可靠性。为此,MultcastDelegate类提供了一个实例方法GetInvocationList,它用于显式调用链中的每一个委托,并使用符合需求的任意算法。【例9-6】手工迭代。internalsealedclassLight{publicStringSwitchPosition(){return"TheLightisoff";}}internalsealedclassFan{publicStringSpeed(){thrownewInvalidOperationException("Thefanbrokeduetooverheating");}}internalsealedclassSpeaker{publicStringVolume(){return"Thevolumeisloud";}}9.1.6手工迭代publicsealedclassprogram{privatedelegateStringGetStatus();//声明一个委托
publicstaticvoidMain(){GetStatusgetStatus=null;getStatus+=newGetStatus(newLight().SwitchPosition);getStatus+=newGetStatus(newFan().Speed);getStatus+=newGetStatus(newSpeaker().Volume);Console.WriteLine(GetComponentStatusReport(getStatus));}privatestaticStringGetComponentStatusReport(GetStatusstatus){if(status==null)returnnull;StringBuilderreport=newStringBuilder();Delegate[]arrayOfDelegates=status.GetInvocationList();foreach(GetStatusgetStatusinarrayOfDelegates){try{report.AppendFormat("{0}{1}{1}",getStatus(),Environment.NewLine);}catch(Exceptione){objectcomponent=getStatus.Target;report.AppendFormat("Failedtogetstatusfrom{1}{2}{0}Error:{3}{0}{0}",Environment.NewLine,((component==null)?"":component.GetType()+"."),getStatus.Method.Name,e.Message);}}returnreport.ToString();}}
9.1.6手工迭代1.返回类型协变publicclassDelegateReturn{}publicclassDelegateReturn2:DelegateReturn{}publicdelegateDelegateReturnMyDelegate();classProgram{staticvoidMain(){MyDelegatemyDelegate=Method;myDelegate();}staticDelegateReturn2Method(){DelegateReturn2result=newDelegateReturn2();returnresult;}}9.1.7协变和抗变2.参数类型抗变publicclassDelegateP
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- u盘供货合同范本
- 住宅赠予合同范本
- 农业种子买卖协议合同范本
- 化妆服务合同范本简易
- 业务指导合同范本
- 2024年招商银行呼和浩特分行招聘考试真题
- 加盟学员签约合同范本
- 买土地合同范本
- 加油站聘用站长合同范本
- 借款项目合同范本
- 汽车运行材料ppt课件(完整版)
- 四年级数学下册教案-练习一-北师大版
- GB∕T 1732-2020 漆膜耐冲击测定法
- 2022《化工装置安全试车工作规范》精选ppt课件
- Q∕GDW 12067-2020 高压电缆及通道防火技术规范
- 汽车系统动力学-轮胎动力学
- 《经济研究方法论》课程教学大纲
- 10T每天生活污水处理设计方案
- 中国民航国内航空汇编航路314系列航线
- 山西特色文化简介(课堂PPT)
- 一元二次方程100道计算题练习(附答案)
评论
0/150
提交评论