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理解委托的概念 publicclassMathOpt publicintAdd intargument1 intargument2 returnargument1 argument2 publicdelegateintMathOptDelegate intvalue1 intvalue2 classProgram staticvoidMain string args 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 classMyClass publicstaticvoidHello strings Console WriteLine 您好 0 s publicstaticvoidGoodbye strings Console WriteLine 再见 0 s classProgram staticvoidMain string args 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 string args MathOptDelegateoppDel MathOptobj newMathOpt oppDel obj Add Console WriteLine oppDel 1 2 输出3 注意 通过委托变量间接调用对象obj的实例方法Add 实际上调用的是MathOptDelegate类的Invoke 方法 这个Invoke 方法从何而来 委托定义语句 publicdelegateintMathOptDelegate intvalue1 intvalue2 当编译器遇到这段代码时 它会产生如下面所示的一个完整的类定义 publicclassMathOptDelegate System MulticastDelegate publicMathOptDelegate Objecttarget Int32methodPtr publicvoidvirtualInvoke Int32value1 Int32value2 publicvirtualIAsyncResultBeginInvoke Int32value1 Int32value2 AsyncCallbackcallback Objectobject publicvirtualvoidEndInvoke IAsyncResultresult 委托定义语句 publicdelegateintMathOptDelegate intvalue1 intvalue2 当编译器遇到这段代码时 它会产生如下面所示的一个完整的类定义 publicclassMathOptDelegate System MulticastDelegate publicMathOptDelegate Objecttarget Int32methodPtr publicvoidvirtualInvoke Int32value1 Int32value2 publicvirtualIAsyncResultBeginInvoke Int32value1 Int32value2 AsyncCallbackcallback Objectobject publicvirtualvoidEndInvoke IAsyncResultresult 类的构造器 它接收两个参数target和methodPtr target 引用要调用方法的对象 methodPtr 是一个方法指针 代表要调用的对象方法 委托定义语句 publicdelegateintMathOptDelegate intvalue1 intvalue2 当编译器遇到这段代码时 它会产生如下面所示的一个完整的类定义 publicclassMathOptDelegate System MulticastDelegate publicMathOptDelegate Objecttarget Int32methodPtr publicvoidvirtualInvoke Int32value1 Int32value2 publicvirtualIAsyncResultBeginInvoke Int32value1 Int32value2 AsyncCallbackcallback Objectobject publicvirtualvoidEndInvoke IAsyncResultresult 方法和源代码中指定的原型一样 对外界对象实例方法的调用通过Invoke 方法实现 委托定义语句 publicdelegateintMathOptDelegate intvalue1 intvalue2 当编译器遇到这段代码时 它会产生如下面所示的一个完整的类定义 publicclassMathOptDelegate System MulticastDelegate publicMathOptDelegate 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 publicclassA publicintf1 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 Delegate ds 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 try x 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 returnx y 在表示减法选项的单选按钮rdoSubtract单击事件中将Subtract 函数赋予委托变量curOpt 再作为DoCalculate 方法的实参传入 privatevoidrdoSubtract CheckedChanged objectsender EventArgse curOpt this Subtract 选择函数DoCalculate curOpt 调用函数 这一示例的关键是将委托作为函数DoCalculate 的参数 从而实现了在程序运行时动态选择要执行的函数 避免了在函数DoCalculate中采用条件语句进行判断 试一试 如果本例不使用委托 修改代码实现同样的功能 然后比对一下两种方案 示例如图所示 每按一次键 程序就回调两个函数显示当前时间 2 利用委托实现回调 定义一个委托 它决定了要被回调的函数签名 publicdelegatevoidShowTime 定义两个要被回调方法的类 classA publicvoidAShowTime System Console WriteLine A DateTime Now classB publicstaticvoidBShowTime System Console WriteLine B DateTime Now 实现代码 类Controller完成回调的核心工作 classController privateShowTimed 用于接收外界对象提供的方法 以实现回调 外界对象将需要回调的方法传入publicvoidRegisterDelegateForCallback ShowTimemethod d method 移除要回调的方法publicvoidUnRegisterDelegateForCallback ShowTimemethod d method 实现回调publicvoidCallBack if d null d Invoke 调用所有回调的方法 使用Controller类的代码如下 staticvoidMain string args 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委托实现定时回调 定义一个类用于向回调函数提供参数信息 classTaskInfo publicintcount 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 string args 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 接着 定义事件发布者与响应者类 事件发布者类publicclassPublisher publicMyMultiDelegatehandlers 事件响应者清单 事件响应者类publicclassSubscriber 事件处理函数publicvoidMyMethod inti Console WriteLine i 1 事件与多路委托 staticvoidMain string args 一个事件源对象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 string args 一个事件源对象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 string args 一个事件源对象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 事件发布者类publicclassPublisher publiceventMyMultiDelegatehandlers 定义一个事件 激发事件publicvoidFireEvent handlers 10 事件响应者类publicclassSubscriber 事件处理函数publicvoidMyMethod inti Console WriteLine i staticvoidMain string args 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

提交评论