“委托”机制的“委托”到底是什么意思呢,《高级汉语大词典》中是如下解释的:托付给别的人或机构办理。要说生活中的意思其实大家都能理解,无非是“当某人(机构)需要完成一件自己不能或不应该完成的事情的时候,此人(机构)物色一个合适的且有能力完成此事的人选,然后提供必要的信息,将此事委托给物色到的人(机构)来完成。”
软件的对象方法其实是对现实世界的模拟,你可能会想现实世界里的委托哪有这么多呢?这么重要呢?其实你也许没有注重到老板把厚厚的一摞资料摔在你的案头让你无论如何在×月×日前交活,这不就是一种委托吗?当然也许没有委托书(和委任状),但它就是委托。这样看委托是不是就非常重要了,它甚至是构成现实社会的基础机制之一。
从这个意义上理解委托机制的重要性我想应该是足够了。委托机制是促使事件发送与事件接受的一种对接策略,对象对四周信号的反应或在一定环境中所具备的对其它对象的通知行为的响应则被描述成所谓的“事件”,这可以类比人对四周世界反馈产生信号的能力。
委托就是一种定向信号流:指定产生、接受信号者并产生信号反馈的技术。
我可爱的小女儿才刚刚学会说话就在饭桌上支使她可怜的“老”爸:爸爸,把我的小勺子拿来。我接到“信号”立即屁颠屁颠地跑到厨房拿到勺子送到女儿的小手上,一个“委托”完成的非常漂亮,而女儿则无须知道我在什么地方、如何拿到勺子,她只管接受到我给她专门买的小勺子就行,否则,她就要仰着小脸“哇哇”大哭了。
先说说事件委托的基础。
为了说明问题我设计了一个窗体还有几个按钮的示例DelegateDemo1项目,以便可以直观感受委托的基本用法。
至于委托在.Net中的实现,编译器会自动生成一个完整的类定义:一个构造器,Invoke,BeginInvoke,以及EndInvoke。
C#2003中隐式使用Invoke(其显式调用会报错)会让我们误解有一个以委托名称为名称的函数,其实没有,它只是隐式使用Invoke方法。而VB.net包括最新的VB.net2005均可以采取两种方法(显式或隐式)使用Invoke方法。我比较喜欢显式使用它,因为这样比较贴近事物的本质。
回到示例,代码如下(使用VB.net2003并出去VS自动生成的代码):
1 Public Class Form1Class Form1
2
3 Inherits System.Windows.Forms.Form
4
5 PRivate Sub Button1_Click()Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click, Button2.Click, Button4.Click
6
7 Select Case CType(sender, Button).Name
8
9 Case "Button1"
10
11 MessageBox.Show("你点击了Button1!", "委托提示", MessageBoxButtons.OK)
12
13 Case "Button2"
14
15 MessageBox.Show("你点击了Button2!", "委托提示", MessageBoxButtons.OK)
16
17 Case "Button3"
18
19 MessageBox.Show("你点击了Button3!", "委托提示", MessageBoxButtons.OK)
20
21 Case "Button4"
22
23 MessageBox.Show("你点击了Button4!", "委托提示", MessageBoxButtons.OK)
24
25 End Select
26
27 End Sub
28
29 Private Sub Button2_Click()Sub Button2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button2.Click
30
31 MessageBox.Show("Button2的Click事件,注重执行顺序!", "委托提示", MessageBoxButtons.OK)
32
33 End Sub
34
35 Private Sub Button3_Click()Sub Button3_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button3.Click
36
37 Dim Demohandler1 As DemoHandler
38
39 Demohandler1 = AddressOf Button1_Click
40
41 Demohandler1.Invoke(sender, e)
42
43 Dim Demohandler2 As DemoHandler
44
45 Demohandler2 = AddressOf Button2_Click
46
47 Demohandler2(sender, e)
48
49
50
51 End Sub
52
53 Private Sub Button4_Click()Sub Button4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button4.Click
54
55 End Sub
56
57 Private Sub Button5_Click()Sub Button5_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button5.Click
58
59 End Sub
60
61 Private Sub Form1_DoubleClick()Sub Form1_DoubleClick(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.DoubleClick, Button5.Click
62
63 If TypeOf sender Is Button Then
64
65 If MessageBox.Show("你确定退出吗?", "退出提示", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) = DialogResult.Yes Then
66
67 Me.Close()
68
69 Else : Exit Sub
70
71 End If
72
73 Else : Me.Close()
74
75 End If
76
77 End Sub
78
79End Class
80
Button1按钮的click事件的代码只需看看Handles子句,不仅有Button1.Click还有Button2.Click, Button4.Click,MSDN上说:在过程声明结尾处使用Handles要害字将使其处理由使用 WithEvents 要害字声明的对象变量所引发的事件。也可以在派生类中使用Handles要害字处理基类的事件。其实Handles子句答应任何与事件的参数签名相符的过程来响应这个事件。这实际上就是委托。在编译时间,.NET Framework用你的事件名称创建一个委托类,只是在结尾添加“EventHandler”字样。换言之,事件是使用委托来实现的,委托是面向对象函数指针的一种形式,它答应通过对函数进行引用的方法来间接地调用该函数。
用Ildasm.exe反汇编DelegateDemo1.exe,请看下图:
双击set_Button1方法或其他set_...方法,你都可以看到有System.EventHandler类出现。.NET Framework 中的事件模型基于具有事件委托,System.EventHandler将事件与事件处理程序连接。
Button1的Click事件过程中的Select Case…End Select功能是根据点击按钮的名称来选择事件处理程序。
Button3的Click事件过程要好好看看的。如下表:
语 句
释 义
Dim Demohandler1 As DemoHandler
定义一个DemoHandler委托的实例,DemoHandler可以换成 EventHandler
Demohandler1 = AddressOf Button1_Click
AddressOf可以认为就是将该委托和Button1_Click绑定
Demohandler1.Invoke(sender, e)
显式使用委托的Invoke方法调用事件处理程序
Dim Demohandler2 As DemoHandler
定义一个DemoHandler委托的实例,DemoHandler2
Demohandler2 = AddressOf Button2_Click
同上AddressOf
Demohandler2(sender, e)
隐式使用委托的Invoke方法调用事件处理程序
Button4按钮的click事件过程为空,因为在Button1_Click的Handles子句中已经注册,且由于它们的事件签名完全一致,故调用Button1_Click事件处理过程。
Button5按钮的click事件过程也为空,不要着急,下面的Form1_DoubleClick事件处理过程的Handles子句中注册了Button5,所以Button5的click事件的事件处理程序调用Form1的DoubleClick事件的事件处理程序。
在Form1的DoubleClick事件处理过程使用对控件类型的判定来选择处理方式。
我不说你也看出来了,这样写代码,就可以将一些本来需要重复多次的代码省去,符合重构的思想,使得代码尽量简约而由不失可读性。
现实生活中委托别人办的事能不能办好取决于以下两个方面:
1、是否委托给了合适的人;
2、是否把解决委托事件所需的正确和必要的信息传递给了它。
比如女儿委托我拿勺子,这个委托是相当成功的,因为她委托给了我(人选合适)且明确要她的小勺(正确和必要的信息传递)。
对应到.Net框架中的委托,一是要有委托对象,二是要签名相符。
项目编译运行后,点击Button3按钮有如下图显示:
点击“确定”后有:
点击“退出”Button5按钮有:
(注:以上内容描述多有个人理解的表述,欢迎大家批评指正,如有时间我会继续写如多路广播委托及使用委托的需求方面的内容)
备注:
引用:(下面这段是我看到过对委托概念及特点最为言简意赅的描述之一)
委托是用来处理其他语言(如 C++、Pascal 和 Modula)需用函数指针来处理的情况的。不过与 C++ 函数指针不同,委托是完全面对对象的;另外,C++ 指针仅指向成员函数,而委托同时封装了对象实例和方法。 委托声明定义一个从 System.Delegate 类派生的类。委托实例封装一个调用列表,该列表列出一个或多个方法,其中每个方法均作为一个可调用实体来引用。对于实例方法,可调用实体由该方法和一个相关联的实例组成。对于静态方法,可调用实体仅由一个方法组成。用一个适当的参数集来调用一个委托实例,就是用此给定的参数集来调用该委托实例的每个可调用实体。
委托实例的一个有趣且有用的属性是:它不知道也不关心它所封装的方法所属的类;它所关心的仅限于这些方法必须与委托的类型兼容(第 15.1 节)。这使委托非常适合于“匿名”调用。——《C#语言规范》Scott Wiltamuth 和 Anders Hejlsberg