NET-第4部分ppt课件_第1页
NET-第4部分ppt课件_第2页
NET-第4部分ppt课件_第3页
NET-第4部分ppt课件_第4页
NET-第4部分ppt课件_第5页
已阅读5页,还剩55页未读 继续免费阅读

下载本文档

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

文档简介

第一部分Microsoft.NET框架基本原理第二部分类型与通用语言运行时第三部分类型设计第四部分基本类型第五部分类型管理,第四部分基本类型文本处理枚举类型与位标记数组接口定制特性委托,第17章委托、事件驱动,17.1委托,委托是一种新的面向对象语言特性。基于委托,开发事件驱动的应用程序变得非常简便。通过灵活地使用委托,.NETFramework设计出了一套异步编程框架,使程序员很方便地开发出具有多线程特性的应用程序。在.NETFramework基类库中,大量地使用了委托(delegate)。委托到底是什么?,我们都很熟悉常用的数据类型(如int)的使用方法:先定义一个变量,然后再给其赋值,如下所示:inti;/定义变量i=100;/给变量赋值委托(delegate)也可以看成是一种数据类型,可以用于定义变量。但它是一种特殊的数据类型,它所定义的变量能接收的数值只能是一个函数,更确切地说,委托类型的变量可以接收一个函数的地址,很类似于C/C+语言的函数指针。简单地说:委托变量可看成是一种类型安全的函数指针,它只能接收符合其要求的函数引用。,17.1.1理解委托的概念,publicclassMathOptpublicintAdd(intargument1,intargument2)returnargument1+argument2;publicdelegateintMathOptDelegate(intvalue1,intvalue2);classProgramstaticvoidMain(stringargs)MathOptDelegateoppDel;MathOptobj=newMathOpt();oppDel=obj.Add;Console.WriteLine(oppDel(1,2);/输出3/Console.ReadKey();,示例1:,从上述示例中可得到一个直观的印象:委托可以看成是一个函数的“容器”,将某一具体的函数“装入”后,就可以把它当成函数一样使用。其实,委托是一个派生自Delegate的类,但从使用角度理解为函数“容器”也是可以的。那么,是不是所有的函数都可以赋值给委托类型MathOptDelegate的变量oppDel呢?注意MathOptDelegate的定义语句:publicdelegateintMathOptDelegate(intvalue1,intvalue2);它规定了委托类型的变量只能接收这样的函数:拥有两个int类型的参数,并且返回值类型也是int。只要是满足上述要求的函数,不管名字如何,也不管是静态的还是实例的,都可以传给委托类型的变量oppDel,并通过oppDel来“间接地”调用它们。,定义委托类型时对函数的要求被称为函数的“签名(Signature)”。函数的签名规定了函数的参数数目和类型,以及函数的返回值,体现了函数的本质特征。每一个委托都确定了一个函数的签名。拥有不同签名的函数不能赋值给同一类型的委托变量。,13.1.2委托的组合与分解,委托不仅可以代表一个函数,还可以组合“一堆”的函数,然后批量执行它们。下面示例2,展示了委托变量之间的组合与分解。delegatevoidMyDelegate(strings);classMyClasspublicstaticvoidHello(strings)Console.WriteLine(您好,0!,s);publicstaticvoidGoodbye(strings)Console.WriteLine(再见,0!,s);,classProgramstaticvoidMain(stringargs)MyDelegatea,b,c,d;/创建引用Hello方法的委托对象aa=MyClass.Hello;Console.WriteLine(调用委托变量a:);a(a);/创建引用Goodbye方法的委托对象bb=MyClass.Goodbye;Console.WriteLine(调用委托变量b:);b(b);,请仔细看以下代码:,/a和b两个委托合成c,c=a+b;Console.WriteLine(调用委托变量c:);c(c=a+b);/c将按顺序调用两个方法/从组合委托c中移除a,只留下b,用d代表移除结果,d=c-a;Console.WriteLine(调用委托变量d:);d(d=c-a);/后者仅调用Goodbye方法:Console.ReadKey();,上述代码中委托变量c组合了两个委托变量a和b,因而,它拥有两个函数,当执行“c(“c=a+b”);”时,将导致MyClass类的两个静态函数都被执行。像c这样的委托变量又称为“多路委托变量”。可以用加法运算符来组合单个委托变量为多路委托变量。也可以使用减法运算符从一个多路委托变量中移除某个委托变量。上述示例2运行结果为:,17.1.3委托揭秘,编译器和CLR怎样来实现委托?使用ildasm查看示例1Main()方法的代码:staticvoidMain(stringargs)MathOptDelegateoppDel;MathOptobj=newMathOpt();oppDel=obj.Add;Console.WriteLine(oppDel(1,2);/输出3注意:通过委托变量间接调用对象obj的实例方法Add(),实际上调用的是MathOptDelegate类的Invoke()方法。这个Invoke()方法从何而来?,委托定义语句:publicdelegateintMathOptDelegate(intvalue1,intvalue2);当编译器遇到这段代码时,它会产生如下面所示的一个完整的类定义:publicclassMathOptDelegate:System.MulticastDelegatepublicMathOptDelegate(Objecttarget,Int32methodPtr);publicvoidvirtualInvoke(Int32value1,Int32value2);publicvirtualIAsyncResultBeginInvoke(Int32value1,Int32value2,AsyncCallbackcallback,Objectobject);publicvirtualvoidEndInvoke(IAsyncResultresult);,委托定义语句:publicdelegateintMathOptDelegate(intvalue1,intvalue2);当编译器遇到这段代码时,它会产生如下面所示的一个完整的类定义:publicclassMathOptDelegate:System.MulticastDelegatepublicMathOptDelegate(Objecttarget,Int32methodPtr);publicvoidvirtualInvoke(Int32value1,Int32value2);publicvirtualIAsyncResultBeginInvoke(Int32value1,Int32value2,AsyncCallbackcallback,Objectobject);publicvirtualvoidEndInvoke(IAsyncResultresult);,类的构造器,它接收两个参数target和methodPtr。target:引用要调用方法的对象;methodPtr:是一个方法指针,代表要调用的对象方法。,委托定义语句:publicdelegateintMathOptDelegate(intvalue1,intvalue2);当编译器遇到这段代码时,它会产生如下面所示的一个完整的类定义:publicclassMathOptDelegate:System.MulticastDelegatepublicMathOptDelegate(Objecttarget,Int32methodPtr);publicvoidvirtualInvoke(Int32value1,Int32value2);publicvirtualIAsyncResultBeginInvoke(Int32value1,Int32value2,AsyncCallbackcallback,Objectobject);publicvirtualvoidEndInvoke(IAsyncResultresult);,方法和源代码中指定的原型一样。对外界对象实例方法的调用通过Invoke()方法实现。,委托定义语句:publicdelegateintMathOptDelegate(intvalue1,intvalue2);当编译器遇到这段代码时,它会产生如下面所示的一个完整的类定义:publicclassMathOptDelegate:System.MulticastDelegatepublicMathOptDelegate(Objecttarget,Int32methodPtr);publicvoidvirtualInvoke(Int32value1,Int32value2);publicvirtualIAsyncResultBeginInvoke(Int32value1,Int32value2,AsyncCallbackcallback,Objectobject);publicvirtualvoidEndInvoke(IAsyncResultresult);,用于实现异步调用。,编译器定义的类中有4个方法:一个构造器、Invoke、BeginInvoke,以及EndInvoke。MathOptDelegate类的方法全部都是虚方法,其对应的方法IL代码为空。以Invoke()方法为例,其生成的IL代码如下(通过ILDasm查看):.methodpublichidebysignewslotvirtualinstanceint32Invoke(int32value1,int32value2)runtimemanaged/endofmethodMathOptDelegate:InvokeC#编译器为委托类型生成的所有方法体都为空!,这个标记告诉CLR,此方法的IL指令将在运行时动态生成。,17.1.4委托调用链,自定义委托其实是从MulticastDelegate类中派生出来的。Delegate类代表委托基类,而MulticastDelegate类代表“多路广播委托”,言下之意是从Delegate类派生出的委托只能“装有”一个函数,而从MulticastDelegate类派生出来的委托则可以“装有”多个函数,这多个函数首尾相接为一个“委托调用链表”,包容于多路委托变量中(见下图)。事实上C#编译器将我们定义的委托类型都处理为从MulticastDelegate派生。,示例3介绍“委托调用链”的含义,首先定义一个委托类型MyDelegate与一个类ApublicdelegateintMyDelegate(intvalue);publicclassApublicintf1(inti)Console.WriteLine(f1.i=0,i);returni;publicintf2(inti)i*=2;Console.WriteLine(f2.i=0,i);returni;,类A中两个方法都符合MyDelegate委托所确定的函数签名。,创建对象a:Aa=newA();创建第一个委托变量s1MyDelegates1=newMyDelegate(a.f1);s1+=newMyDelegate(a.f2);Delegate类中有一个GetInvocationList()静态方法用于获取委托调用链,可以通过它来了解多路委托变量组合了多少个方法。,在Main()函数中测试“委托调用链”:,提示:上面两句可以简写为以下形式:MyDelegates1=a.f1;s1+=a.f2;,Delegateds;ds=s1.GetInvocationList();Console.WriteLine(S1的方法调用列表中包含0个方法,ds.GetLength(0);上述代码运行结果:S1的方法调用列表中包含了2个方法如果在代码中调用委托变量,将导致委托调用链中的所有方法顺序执行:s1(5);运行结果:f1.i=5f2.i=10创建第2个多路委托变量s2:MyDelegates2=newMyDelegate(a.f1);s2+=newMyDelegate(a.f2);,可将两个委托变量s1和s2组合为一个大的委托变量mul,mul的委托调用链为s1和s2的委托调用链的头尾相连而成。/组合委托Delegatemul;/mul=System.Delegate.Combine(s1,s2);mul=s1+s2;ds=mul.GetInvocationList();Console.WriteLine(mul的方法调用列表中包含0个方法,ds.GetLength(0);上述代码运行结果:mul的方法调用列表中包含4个方法如果委托定义的函数有返回值,则多路委托变量的返回值为委托调用链中最后一个方法的返回值,中间调用的方法其返回值会被丢弃。,intret=(mulasMyDelegate)(10);Console.WriteLine(ret=0,ret);上述代码运行结果:f1.i=10f2.i=20f1.i=10f2.i=20ret=20,17.1.5在编程中使用委托,1.使用委托动态调用方法,示例如图所示,输入两个操作数,再选择运算方法,程序即可算出结果。,首先声明一个委托类型Calculate:publicdelegatedoubleCalculate(doublex,doubley);然后在窗体中定义一个私有字段curOpt,表明当前选中的操作类型。privateCalculatecurOpt;以下函数为整个程序的核心:/完成计算工作,实现细节,voidDoCalculate(Calculatecalmethod)doublex,y;tryx=Convert.ToDouble(txtNumber1.Text);y=Convert.ToDouble(txtNumber2.Text);lblInfo.Text=String.Format(结果:0,calmethod(x,y);catch(Exceptione)lblInfo.Text=String.Format(“结果:0”,e.Message);函数的参数是Calculate委托类型。通过委托变量动态地调用“加”、“减”、“乘”、“除”4个方法之一。,以减法为例,首先需定义一个完成减法操作的函数:doubleSubtract(doublex,doubley)returnxy;在表示减法选项的单选按钮rdoSubtract单击事件中将Subtract()函数赋予委托变量curOpt,再作为DoCalculate()方法的实参传入。privatevoidrdoSubtract_CheckedChanged(objectsender,EventArgse)curOpt=this.Subtract;/选择函数DoCalculate(curOpt);/调用函数,这一示例的关键是将委托作为函数DoCalculate()的参数,从而实现了在程序运行时动态选择要执行的函数,避免了在函数DoCalculate中采用条件语句进行判断。试一试:如果本例不使用委托,修改代码实现同样的功能,然后比对一下两种方案。,示例如图所示,每按一次键,程序就回调两个函数显示当前时间。,2.利用委托实现回调,定义一个委托,它决定了要被回调的函数签名。publicdelegatevoidShowTime();定义两个要被回调方法的类:classApublicvoidAShowTime()System.Console.WriteLine(A:+DateTime.Now);classBpublicstaticvoidBShowTime()System.Console.WriteLine(B:+DateTime.Now);,实现代码:,类Controller完成回调的核心工作。classControllerprivateShowTimed;/用于接收外界对象提供的方法,以实现回调/外界对象将需要回调的方法传入publicvoidRegisterDelegateForCallback(ShowTimemethod)d+=method;/移除要回调的方法publicvoidUnRegisterDelegateForCallback(ShowTimemethod)d-=method;/实现回调publicvoidCallBack()if(d!=null)d.Invoke();/调用所有回调的方法,使用Controller类的代码如下:staticvoidMain(stringargs)Aa=newA();/创建被回调方法的对象Controllerc=newController();/注册两个回调方法c.RegisterDelegateForCallback(a.AShowTime);c.RegisterDelegateForCallback(B.BShowTime);Console.WriteLine(敲任意键显示当前时间,ESC键退出.);while(Console.ReadKey(true).Key!=ConsoleKey.Escape)c.CallBack();/回调上述代码向回调列表中增加了两个方法,每次按一个键,就回调一次。,可根据需要调用UnRegisterDelegateForCallback()从回调方法列表中移除一个方法:c.UnRegisterDelegateForCallback(a.AShowTime);则回调将只执行剩余的那个方法BShowTime()了。,如果需要以固定的时间间隔调用某个函数,则可以使用.NETFramework提供的TimerCallBack委托,其定义如下:publicdelegatevoidTimerCallback(Objectstate)示例如图所示,程序每隔一秒钟自动输出当前的时间。,3.使用TimerCallBack委托实现定时回调,定义一个类用于向回调函数提供参数信息。classTaskInfopublicintcount=0;被回调的函数如下:staticvoidShowTime(Objectti)TaskInfoobj=tiasTaskInfo;obj.count+;System.Console.WriteLine(0)1,obj.count,DateTime.Now);注意:此函数的形式满足TimerCallBack委托的要求。,实现代码:,要想通过TimerCallBack委托定时地调用回调函数,必须创建一个Time对象。Timer类的构造函数如下:publicTimer(TimerCallbackcallback,Objectstate,intdueTime,intperiod)其参数简述如下:callback:要定时回调的函数;state:向回调函数传送的参数;dueTime:调用回调函数之前迟延的时间量(以毫秒为单位)。设为0则立即启动计时器;period:每隔多少毫秒调用一次回调函数。,Main()函数如下:staticvoidMain(stringargs)System.Console.WriteLine(敲任意键结束);TaskInfoti=newTaskInfo();/创建Timer对象,将一个回调函数传给它,每隔一秒调用一次Timertm=newTimer(ShowTime,ti,0,1000);System.Console.ReadKey();tm.Dispose();,示例如图所示,单击从窗体中的按钮,主窗体会记录下对按钮的单击次数。,4.使用委托实现多窗体通信,主、从窗体间信息的传送是通过委托进行的。以下代码定义了委托类型:publicdelegatevoidShowInfo(stringinfo);在从窗体中定义了一个公有的委托类型字段recorder:publicShowInforecorder;/记录信息的“记录员”在从窗体按钮的单击事件中,累加计数器,并通知“记录员(recorder)”计数。privateintcounter=0;/计数器privatevoidbtnClickMe_Click(objectsender,EventArgse)counter+;if(recorder!=null)recorder(counter.ToString();,主窗体中提供一个方法用于计数:/用标签显示信息,注意,此方法可以为Private的privatevoidShowCount(Stringcount)lblCount.Text=count;然后,在主窗体的构造函数中创建从窗体,并将ShowCount()方法传给从窗体的recorder字段。publicfrmMain()InitializeComponent();/创建从窗体对象并显示frmOtherfrm=newfrmOther();frm.recorder=this.ShowCount;/向从窗体的委托变量赋值frm.Show();,试一试:利用多路委托的特性,将本节示例修改为:一个主窗体统计多个从窗体的按钮单击次数,或者是在一个窗体上单击按钮,有多个计数窗体同时显式单击次数。,事件的激发意味着某个条件的满足。事件是由对象发出的消息,它是一个信号,通知应用程序有事情发生。激发与响应事件的载体都是对象。激发事件的对象被称为“事件源”,对这个事件进行响应的对象被称为“响应者”,响应者会提供一个专门的函数,称为“事件处理程序”,对特定的事件进行响应。一个事件可以有多个响应者,也可以一个响应者都没有。当某一事件发生时,计算机会检查有无响应者,如果有,调用它所提供的事件处理程序。.NETFramework采用统一的方式对事件处理程序命名,即用下划线将事件源对象和事件名称组合起来。,17.2事件驱动,事件的主要特点是一对多关联,即一个事件源,多个响应者。在具体技术上,.NETFramework的事件处理机制是基于多路委托实现的。,17.2.1.NETFramework的事件处理机制,示例:首先定义一个委托:publicdelegatevoidMyMultiDelegate(intvalue);接着,定义事件发布者与响应者类:/事件发布者类publicclassPublisherpublicMyMultiDelegatehandlers;/事件响应者清单/事件响应者类publicclassSubscriber/事件处理函数publicvoidMyMethod(inti)Console.WriteLine(i);,1.事件与多路委托,staticvoidMain(stringargs)/一个事件源对象Publisherp=newPublisher();/两个事件响应者Subscribers1=newSubscriber();Subscribers2=newSubscriber();/可以直接调用Delegate类的静态方法组合多个委托p.handlers=System.Delegate.Combine(p.handlers,newMyMultiDelegate(s1.MyMethod)asMyMultiDelegate;p.handlers=System.Delegate.Combine(p.handlers,newMyMultiDelegate(s2.MyMethod)asMyMultiDelegate;/直接调用委托变量,代表激发事件p.handlers(10);,模拟实现事件响应的代码:,staticvoidMain(stringargs)/一个事件源对象Publisherp=newPublisher();/两个事件响应者Subscribers1=newSubscriber();Subscribers2=newSubscriber();/可以直接调用Delegate类的静态方法组合多个委托p.handlers=System.Delegate.Combine(p.handlers,newMyMultiDelegate(s1.MyMethod)asMyMultiDelegate;p.handlers=System.Delegate.Combine(p.handlers,newMyMultiDelegate(s2.MyMethod)asMyMultiDelegate;,模拟实现事件响应的代码:,/p.handlers+=newMyMultiDelegate(s1.MyMethod);/p.handlers+=newMyMultiDelegate(s2.MyMethod);/p.handlers+=s1.MyMethod;/p.handlers+=s2.MyMethod;,staticvoidMain(stringargs)/一个事件源对象Publisherp=newPublisher();/两个事件响应者Subscribers1=newSubscriber();Subscribers2=newSubscriber();/可以直接调用Delegate类的静态方法组合多个委托p.handlers=System.Delegate.Combine(p.handlers,newMyMultiDelegate(s1.MyMethod)asMyMultiDelegate;p.handlers=System.Delegate.Combine(p.handlers,newMyMultiDelegate(s2.MyMethod)asMyMultiDelegate;/直接调用委托变量,代表激发事件p.handlers(10);,模拟实现事件响应的代码:,上述代码执行到最后一句时,将会调用两个事件响应者s1和s2的事件响应函数MyMethod,在控制台窗口输出两个整数:1010上面例子中。事件的激发是在Main()函数中引发的,而真实的事件不允许由外界引发,必须由事件源对象自己引发。为了限制事件的激发只能由事件源对象自己引发,C#引入了一个新的关键字event,为此需修改上面示例。,publicdelegatevoidMyMultiDelegate(intvalue);/事件发布者类publicclassPublisherpubliceventMyMultiDelegatehandlers;/定义一个事件/激发事件publicvoidFireEvent()handlers(10);/事件响应者类publicclassSubscriber/事件处理函数publicvoidMyMethod(inti)Console.WriteLine(i);,staticvoidMain(stringargs)Publisherp=newPublisher();Subscribers1=newSubscriber();Subscribers2=newSubscriber();/声明为事件的委托无法直接调用Combine方法/p.handlers=System.Delegate.Combine(p.handlers,newMyMultiDelegate(s1.MyMethod)asMyMultiDelegate;/p.handlers=System.Delegate.Combine(p.handlers,newMyMultiDelegate(s2.MyMethod)asMyMultiDelegate;/必须使用运算符给事件追加委托p.handlers+=s1.MyMethod;p.handlers+=s2.MyMethod;/声明为事件的委托也不能直接调用,下面这句无法通过编译/p.handlers(10);/只能通过类的公有方法间接地引发事件p.FireEvent();,模拟实现事件响应的代码:,对比两个示例,不难看出事件与多路委托其实大同小异,只不过多路委托允许事件源对象之外激发事件罢了。,17.2.2事件应用实例,1.一个事件动态切换多个响应程序,示例如图所示,程序初启时,没有挂接任何事件处理程序,所以,单击“激发事件”按钮不会有任何响应。单击窗体上的任意一个单选钮之后,再次单击“激发事件”按

温馨提示

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

评论

0/150

提交评论