本篇文章主要采用理论和代码实例相结合方法来论述委托和事件,涉及到一些边界技术,如软件架构的 OCP 原则 (开 - 闭原则),
软件架构解耦,设计模式 (Sender-Order) 和事件驱动模型,有一定难度和深度,不适合初级者.
第一部份 委托
关于委托内容,大致为围绕下图来论述.
一 委托是什么 (what)
(一)委托产生的背景之一
1. 我们先来假设这样一个情景需求:
设计一个系统,使其满足如下条件:
(1)当前,只有中国人和英国人使用该系统;
(2)向系统输入用户名和相应的语言,将产生相应语言的问候语;
(3)后期,可能会有其他国家使用该功能 ;
2. 技术方案实现
关于技术方案实现,我们可以采用下图中的三种方式之一.
为了更好地叙述委托,我们分别实现三种技术方案,并找出它们的关系.
2.1 一般实现
Code(控制台程序)
View Code
Result
分析
2.2 用接口实现
如上,我们分析了方案一中的问题,为了更好地解决方案一存在的问题,我们采用面向接口编程的形式来实现.
2.2.1 什么是面向接口编程?
面向接口编程,主要是解决软件架构设计中 "动静问题",即封装不变 (静),剥离变化 (抽出变化).
Code:
View Code
result
分析:
(1) 如上,我们将变化因子 "语言" 剥离出来,形成接口,以后只要每增加一个语言,只需实现接口即可,满足了 OCP 原则,基本解决了方案一中存在的问题;
(2) 如上代码只是为了演示面向接口编程这个功能,并不完善,感兴趣的读者,可自行完善(如将语言定义为枚举类型等);
方案二中的代码,细心的读者会发现,Main 方法中 new 了三个对象,假若以后系统有 300 门语言,那岂不 New 300 个类,这样的话,也不利于代码维护呀,怎么解决这个问题呢?(提示一下,采用设计模式抽象工厂即可解决该问题)
2.3 用委托实现
在这里,没接触过委托的读者,先跳过这部分,往下读,看完(三)怎样使用委托 (How to use) 后,再来看本部分.
Code
View Code
Result
2.3 分析上述三种实现方案的关系
通过上诉三种方式的比较,我们容易得出委托的如下结论:
(1) 抽象方法,屏蔽方法细节,调用只需传递方法名字即可;
(2) 能够实现程序的解耦,松耦合(在方案一中,GetGreetingContens 方法体内 new 了 GreetToUsers 对象,强耦合)
(3) 委托一般扮演中间者的角色,这功能在委托事件中体现非常明显 (第二部分 事件 将详细论述)
如我们在房子,可以直接找房东 (技术实现的一般方法,强耦合),也可找中介 (技术实现的委托,松耦合)
2.4 委托背景概述
委托的重要产生背景,就是事件驱动模型 (关于什么是事件和事件驱动,在本文第二部份 事件 论述).
(二) 委托定义
用 delegate 关键字定义委托(注意,委托是没有方法体的,类似接口里面的方法),在定义委托前,必须明确两个问题:
1. 委托将要绑定的方法;
2. 委托的形参类型,形参个数和委托的返回值必须与将要绑定的方法的形参类型,形参个数和返回值一致;
(三)相关概念
委托涉及的相关概念有函数指针,类型安全性,事件,Lambda 表达式等
1. 函数指针:在 C++ 中,指针的一个类别,主要指向函数(变量指针,主要指向变量地址),可以把 C# 中的委托理解为函数指针;
2. 类型安全性:在 C++ 中,我们都知道指针是类型不安全的 (返回值,返回类型和什么时候返回,这些都是未知的),而委托是类型安全的;
3. 事件:可以把事件理解为委托的一种特例 (在本文第二部份 事件 论述)
4.Lambda 表达式:委托与 Lambd 表达式相结合,实现高效编程,与 Jquery 的 "较少代码做更多的事" 类似,委托与 Lambda,Linq 相结合,使较短代码就能实现比较复杂的功能 (在本篇文章中不讲解 Lambda 与 Lambda 树,将在后续文章中讲解)
(四)委托组成
大致分为两部分:声明委托和注册方法 (也叫绑定方法)
1. 声明委托
用 delegate 声明;
2. 绑定方法
绑定具体方法,传递方法名称;
(五) 委托种类
委托种类,一般分为多播委托和单播委托
1. 单播委托:绑定单个方法
2. 绑定多个方法
(六) 委托操作
1. 绑定方法
2. 解绑方法
二 委托能解决什么问题 (Can do)
1. 避免核心方法中存在大量的 if....else.... 语句 (或 swich 开关语句);
2. 满足程序设计的 OCP 原则;
3. 使程序具有扩展性;
4. 绑定事件;
5. 结合 Lambda 表达式,简化代码,高效编程;
6. 实现程序的松耦合 (解耦),这个在事件(event) 中体现比较明显;
三 怎么使用委托 (How to use)(本篇文章不谈匿名委托,匿名委托具体内容,将在 Lambda 章节讲解)
(一)委托的基本构成
通常地,使用委托的步骤与使用类的步骤是一样的.大致分为两步:定义委托和绑定方法(传递方法)
1. 定义委托
用 delegate 关键字定义委托(注意,委托是没有方法体的,类似接口里面的方法),在定义委托前,必须明确两个问题:
(1). 委托将要绑定的方法;
(2). 委托的形参类型,形参个数和委托的返回值必须与将要绑定的方法的形参类型,形参个数和返回值一致;
public delegate委托返回类型委托名 (形参)
例子:如上我们委托将要表示系统输出的问候语
a. 委托将要绑定的方法
public string ChinesePeople(string UserName)
{
string GreetContents = "您好!" + UserName;
return GreetContents;
}
//English People
public string EnglishPeople(string UserName)
{
string GreetContents = "Hello," + UserName + "!";
return GreetContents;
}
//非英非汉
public string OtherPeople(string UserName)
{
return "Sorrry,当前系统只支持汉语与英语";
}
b. 由如上方法可看出,方法的返回类型为 string, 方法有一个 string 类型的形参,在定义委托时,与其保持一致即可
//定义委托
public delegate string DelegateGetGreeting(string UserName);
2. 绑定方法
使用委托时,将方法名字作为参数传递给委托即可
GreetToUsers greetToUsers = new GreetToUsers();
GetGreetingContents(UserName, greetToUsers.ChinesePeople)
(二)委托绑定方法
1. 绑定单个方法
绑定单个方法,将单个方法名字传给委托即可
View Code
另一种不太规范写法:不用 GetGreetingContents(string UserName,DelegateGetGreeting delegateGetGreeting) 方法
View Code
之所以不规范,主要是在项目中,不利于代码的模块化.
2. 绑定多个方法(多播委托)
注意:绑定多个方法时,委托范围类型必须为 void 类型,否则只返回最后一个绑定的值.
绑定多个方法,采用 += 绑定
View Code
3. 解绑方法
解载绑定的方法,采用 -= 解绑
View Code
(三)委托机制
将如下代码通过反汇编工具. NET Reflector 反汇编
View Code
反汇编
分析:
1. 三个核心方法:BeginInvoke,EndInvoke 和 Invoke
(1) 使用 Invoke 完成一个委托方法的封送,就类似于使用 SendMessage 方法来给界面线程发送消息,是一个同步方法.也就是说在 Invoke 封送的方法被执行完毕前,Invoke 方法不会返回,从而调用者线程将被阻塞.
(2 使用 BeginInvoke 方法封送一个委托方法,类似于使用 PostMessage 进行通信,这是一个异步方法.也就是该方法封送完毕后马上返回,不会等待委托方法的执行结束,调用者线程将不会被阻塞.但是调用者也
可以使用 EndInvoke 方法或者其它类似 WaitHandle 机制等待异步操作的完成.
总结:但是在内部实现上,Invoke 和 BeginInvoke 都是用了 PostMessage 方法,从而避免了 SendMessage 带来的问题.而 Invoke 方法的同步阻塞是靠 WaitHandle 机制来完成的.
提示:
最近浏览一篇文章,也讲得不错:http://blog.csdn.net/goodshot/article/details/6157529
要想深入了解,请参照《CLR Via C#》,
第二部分 事件
关于事件 (event), 将会从如下四个角度来分析.
1. 什么是事件
2. 事件能解决什么问题
3. 怎么使用事件
4. 事件机制
一 什么是事件
谈到委托,必提事件,事件本质是对委托的封装,对外提供 add_EventName(对应 +=) 和 remove_EventName(对应 -=) 访问,从而实现类的封装性.
1. 种类
强类型事件和弱类型事件
2. 一些用处
(1)webForm 控件的 Click 事件.做过 WebForm 开发的朋友,可能对事件是非常熟悉的,如拖一个 Button,双击,就自动在后台生成 Button 的 Click 事件,如下图所示.
原理:在 Windows 运用程序中,Button 类提供了 Click 事件,其本质就是委托,当我们触发 Click 事件时,调用的处理程序方法需要参数,其参数就是由委托类型来定义的.
(2) 设计模式发布 / 订阅.事件是基于委托的,为委托提供了一种发布 / 订阅机制.
二 事件能解决哪些问题
1. 将公有的委托变量定义为私有变量,从而满足类的封装性原则;
2. 具有委托具有的作用;
三 如何使用事件
1. 声明委托
public delegate void DelegateGetGreeting(string UserName);
2. 声明事件
与委托声明一样,只不过多了一个关键字 event
public event DelegateGetGreeting EventGreet;
3. 时间注册方法
事件注册方法与委托注册方法是一样的.
DelegateGreet DG = new DelegateGreet();
//DG.delegateGetGreeting = GTU.ChinesePeople;//注册方法
DG.EventGreet += GTU.ChinesePeople;
DG.EventGreet += GTU.EnglishPeople;
4. 调用事件
调用定义事件的方法
DG.GreetUser("小王");
完整代码如下:
View Code
四 事件机制
事件的本质就是委托,向外提供两个访问方法 add_EventName(对应 +=) 和 remove-EventName(对应 -=),我们通过. NET Reflector 反汇编工具来查看,到底是不是这样的.
参考文献
[01] C# 高级编程(第七版) (Christian Nagel,Bill Evjen 和 Jay Glynn 编著,李铭 译,黄静 审校)
来源: https://www.cnblogs.com/wangjiming/p/8300103.html