NET-第4部分ppt课件

第一部分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 Int32Ptr publicvoidvirtualInvoke Int32value1 Int32value2 publicvirtualIAsyncResultBeginInvoke Int32value1 Int32value2 AsyncCallbackcallback Objectobject publicvirtualvoidEndInvoke IAsyncResultresult 委托定义语句 publicdelegateintMathOptDelegate intvalue1 intvalue2 当编译器遇到这段代码时 它会产生如下面所示的一个完整的类定义 publicclassMathOptDelegate System MulticastDelegate publicMathOptDelegate Objecttarget Int32Ptr publicvoidvirtualInvoke Int32value1 Int32value2 publicvirtualIAsyncResultBeginInvoke Int32value1 Int32value2 AsyncCallbackcallback Objectobject publicvirtualvoidEndInvoke IAsyncResultresult 类的构造器 它接收两个参数target和Ptr target 引用要调用方法的对象 Ptr 是一个方法指针 代表要调用的对象方法 委托定义语句 publicdelegateintMathOptDelegate intvalue1 intvalue2 当编译器遇到这段代码时 它会产生如下面所示的一个完整的类定义 publicclassMathOptDelegate System MulticastDelegate publicMathOptDelegate Objecttarget Int32Ptr publicvoidvirtualInvoke Int32value1 Int32value2 publicvirtualIAsyncResultBeginInvoke Int32value1 Int32value2 AsyncCallbackcallback Objectobject publicvirtualvoidEndInvoke IAsyncResultresult 方法和源代码中指定的原型一样 对外界对象实例方法的调用通过Invoke 方法实现 委托定义语句 publicdelegateintMathOptDelegate intvalue1 intvalue2 当编译器遇到这段代码时 它会产生如下面所示的一个完整的类定义 publicclassMathOptDelegate System MulticastDelegate publicMathOptDelegate Objecttarget Int32Ptr publicvoidvirtualInvoke Int32value1 Int32value2 publicvirtualIAsyncResultBeginInvoke Int32value1 Int32value2 AsyncCallbackcallback Objectobject publicvirtualvoidEndInvoke IAsyncResultresult 用于实现异步调用 编译器定义的类中有4个方法 一个构造器 Invoke BeginInvoke 以及EndInvoke MathOptDelegate类的方法全部都是虚方法 其对应的方法IL代码为空 以Invoke 方法为例 其生成的IL代码如下 通过ILDasm查看 publichidebysignewslotvirtualinstanceint32Invoke int32value1 int32value2 runtimemanaged endofMathOptDelegate 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个方法如果在代码中调用委托变量 将导致委托调用链中的所有方法