NET课件--第4部分.ppt
- 文档编号:17051434
- 上传时间:2023-07-21
- 格式:PPT
- 页数:60
- 大小:534.50KB
NET课件--第4部分.ppt
《NET课件--第4部分.ppt》由会员分享,可在线阅读,更多相关《NET课件--第4部分.ppt(60页珍藏版)》请在冰点文库上搜索。
第一部分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.事件与多路委托,staticvoid
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- NET 课件 部分