“
委托
”
机制的
“
委托
”
到底是什么意思呢,《高级汉语大词典》中是如下解释的:托付给别的人或机构办理。要说生活中的意思其实大家都能理解,无非是
“
当某人(机构)需要完成一件自己不能或不应该完成的事情的时候,此人(机构)物色一个合适的且有能力完成此事的人选,然后提供必要的信息,将此事委托给物色到的人(机构)来完成。
”
软件的对象方法其实是对现实世界的模拟,你可能会想现实世界里的委托哪有这么多呢?这么重要呢?其实你也许没有注意到老板把厚厚的一摞资料摔在你的案头让你无论如何在×月×日前交活,这不就是一种委托吗?当然也许没有委托书(和委任状),但它就是委托。这样看委托是不是就非常重要了,它甚至是构成现实社会的基础机制之一。
从这个意义上理解委托机制的重要性我想应该是足够了。委托机制是促使事件发送与事件接受的一种对接策略,对象对周围信号的反应或在一定环境中所具备的对其它对象的通知行为的响应则被描述成所谓的
“
事件
”
,这可以类比人对周围世界反馈产生信号的能力。
委托就是一种定向信号流:指定产生、接受信号者并产生信号反馈的技术。
我可爱的小女儿才刚刚学会说话就在饭桌上支使她可怜的
“
老
”
爸:爸爸,把我的小勺子拿来。我接到
“
信号
”
立即屁颠屁颠地跑到厨房拿到勺子送到女儿的小手上,一个
“
委托
”
完成的非常漂亮,而女儿则无须知道我在什么地方、如何拿到勺子,她只管接受到我给她专门买的小勺子就行,否则,她就要仰着小脸
“
哇哇
”
大哭了。
先说说事件委托的基础。
为了说明问题我设计了一个窗体还有几个按钮的示例DelegateDemo1项目,以便可以直观感受委托的基本用法。
至于委托在.net中的实现,编译器会自动生成一个完整的类定义:一个构造器,Invoke,BeginInvoke,以及EndInvoke。
C#中隐式使用Invoke(其显式调用会报错)会让我们误解有一个以委托名称为名称的函数,其实没有,它只是隐式使用Invoke方法。而VB.net包括最新的VB.net2005均可以采取两种方法(显式或隐式)使用Invoke方法。我比较喜欢显式使用它,因为这样比较贴近事物的本质。
回到示例,代码如下:
Public Class Form1Class Form1
Inherits System.Windows.Forms.Form
Private Sub Button1_Click()Sub Button1_Click(ByVal sender As System.Object,_
ByVal e As System.EventArgs) Handles Button1.Click,_
Button2.Click, Button4.Click
Select Case CType(sender, Button).Name
Case "Button1"
MessageBox.Show("你点击了Button1!", "委托提示", MessageBoxButtons.OK)
Case "Button2"
MessageBox.Show("你点击了Button2!", "委托提示", MessageBoxButtons.OK)
Case "Button3"
MessageBox.Show("你点击了Button3!", "委托提示", MessageBoxButtons.OK)
Case "Button4"
MessageBox.Show("你点击了Button4!", "委托提示", MessageBoxButtons.OK)
End Select
End Sub
Private Sub Button2_Click()Sub Button2_Click(ByVal sender As System.Object,_
ByVal e As System.EventArgs) Handles Button2.Click
MessageBox.Show("Button2的Click事件,注意执行顺序!", "委托提示", MessageBoxButtons.OK)
End Sub
Private Sub Button3_Click()Sub Button3_Click(ByVal sender As System.Object,_
ByVal e As System.EventArgs) Handles Button3.Click
Dim Demohandler1 As DemoHandler
Demohandler1 = AddressOf Button1_Click
Demohandler1.Invoke(sender, e)
Dim Demohandler2 As DemoHandler
Demohandler2 = AddressOf Button2_Click
Demohandler2(sender, e)
End Sub
Private Sub Button4_Click()Sub Button4_Click(ByVal sender As System.Object,_
ByVal e As System.EventArgs) Handles Button4.Click
End Sub
Private Sub Button5_Click()Sub Button5_Click(ByVal sender As System.Object,_
ByVal e As System.EventArgs) Handles Button5.Click
End Sub
Private Sub Form1_DoubleClick()Sub Form1_DoubleClick(ByVal sender As Object,_
ByVal e As System.EventArgs) Handles MyBase.DoubleClick, Button5.Click
If TypeOf sender Is Button Then
If MessageBox.Show("你确定退出吗?", "退出提示",_
MessageBoxButtons.YesNo, MessageBoxIcon.Warning) = DialogResult.Yes Then
Me.Close()
Else : Exit Sub
End If
Else : Me.Close()
End If
End Sub
End Class Button1按钮的click事件的代码只需看看Handles子句,不仅有Button1.Click还有Button2.Click, Button4.Click,MSDN上说:在过程声明结尾处使用Handles关键字将使其处理由使用 WithEvents 关键字声明的对象变量所引发的事件。也可以在派生类中使用Handles关键字处理基类的事件。其实Handles子句允许任何与事件的参数签名相符的过程来响应这个事件。这实际上就是委托。在编译时间,.NET Framework用你的事件名称创建一个委托类,只是在结尾添加 “ EventHandler ” 字样。换言之,事件是使用委托来实现的,委托是面向对象函数指针的一种形式,它允许通过对函数进行引用的方法来间接地调用该函数。
Inherits System.Windows.Forms.Form
Private Sub Button1_Click()Sub Button1_Click(ByVal sender As System.Object,_
ByVal e As System.EventArgs) Handles Button1.Click,_
Button2.Click, Button4.Click
Select Case CType(sender, Button).Name
Case "Button1"
MessageBox.Show("你点击了Button1!", "委托提示", MessageBoxButtons.OK)
Case "Button2"
MessageBox.Show("你点击了Button2!", "委托提示", MessageBoxButtons.OK)
Case "Button3"
MessageBox.Show("你点击了Button3!", "委托提示", MessageBoxButtons.OK)
Case "Button4"
MessageBox.Show("你点击了Button4!", "委托提示", MessageBoxButtons.OK)
End Select
End Sub
Private Sub Button2_Click()Sub Button2_Click(ByVal sender As System.Object,_
ByVal e As System.EventArgs) Handles Button2.Click
MessageBox.Show("Button2的Click事件,注意执行顺序!", "委托提示", MessageBoxButtons.OK)
End Sub
Private Sub Button3_Click()Sub Button3_Click(ByVal sender As System.Object,_
ByVal e As System.EventArgs) Handles Button3.Click
Dim Demohandler1 As DemoHandler
Demohandler1 = AddressOf Button1_Click
Demohandler1.Invoke(sender, e)
Dim Demohandler2 As DemoHandler
Demohandler2 = AddressOf Button2_Click
Demohandler2(sender, e)
End Sub
Private Sub Button4_Click()Sub Button4_Click(ByVal sender As System.Object,_
ByVal e As System.EventArgs) Handles Button4.Click
End Sub
Private Sub Button5_Click()Sub Button5_Click(ByVal sender As System.Object,_
ByVal e As System.EventArgs) Handles Button5.Click
End Sub
Private Sub Form1_DoubleClick()Sub Form1_DoubleClick(ByVal sender As Object,_
ByVal e As System.EventArgs) Handles MyBase.DoubleClick, Button5.Click
If TypeOf sender Is Button Then
If MessageBox.Show("你确定退出吗?", "退出提示",_
MessageBoxButtons.YesNo, MessageBoxIcon.Warning) = DialogResult.Yes Then
Me.Close()
Else : Exit Sub
End If
Else : Me.Close()
End If
End Sub
End Class Button1按钮的click事件的代码只需看看Handles子句,不仅有Button1.Click还有Button2.Click, Button4.Click,MSDN上说:在过程声明结尾处使用Handles关键字将使其处理由使用 WithEvents 关键字声明的对象变量所引发的事件。也可以在派生类中使用Handles关键字处理基类的事件。其实Handles子句允许任何与事件的参数签名相符的过程来响应这个事件。这实际上就是委托。在编译时间,.NET Framework用你的事件名称创建一个委托类,只是在结尾添加 “ EventHandler ” 字样。换言之,事件是使用委托来实现的,委托是面向对象函数指针的一种形式,它允许通过对函数进行引用的方法来间接地调用该函数。
双击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框架中的委托,一是要有委托对象,二是要签名相符。
引用:
委托是用来处理其他语言(如 C++、Pascal 和 Modula)需用函数指针来处理的情况的。不过与 C++ 函数指针不同,委托是完全面对对象的;另外,C++ 指针仅指向成员函数,而委托同时封装了对象实例和方法。委托声明定义一个从 System.Delegate 类派生的类。委托实例封装一个调用列表,该列表列出一个或多个方法,其中每个方法均作为一个可调用实体来引用。对于实例方法,可调用实体由该方法和一个相关联的实例组成。对于静态方法,可调用实体仅由一个方法组成。用一个适当的参数集来调用一个委托实例,就是用此给定的参数集来调用该委托实例的每个可调用实体。
委托实例的一个有趣且有用的属性是:它不知道也不关心它所封装的方法所属的类;它所关心的仅限于这些方法必须与委托的类型兼容。这使委托非常适合于“匿名”调用。——《C#语言规范》Scott Wiltamuth 和 Anders Hejlsberg