程序员人生 网站导航

VB.net学习笔记(二十三)再识委托

栏目:php教程时间:2016-06-07 15:26:14


1、调用静态方法
1、声明
        拜托须使用前声明定义,可以带参数(1个或多个),可以有返回值。

'位于1个模块或类的声明部份 Delegate Sub OneArgSub{ByVal msg As String) '带1个参数,且无返回类型
        定义了1个拜托的类。后台创建了1个名为OneArgSub的新类,这个类是从System.Delegate类继承而来的。(更准确地说从 Systetn.MuhicastDelegate 继承而来的,而 System.MulticastDelegate 则又是从 System.Delegate 继承而来 的。)
2、实例化
       前面声明新类后,在要使用拜托的功能块中就能够实例化拜托对象:
Dim deleg As OneArgSub '声明类 deleg = New OneArgSub(AddressOf DisplayMsg) '实例化1:用New deleg = AddressOf DisplayMsg '实例化2:直接用AddressOf
      实例化就是让B与C建立联系,通过AddressOf(提取函数指针)把C方法(或函数)的地址赋予B,所以履行B就是终究履行C.
3、完善拜托的事(C,匹配拜托签名的进程)
       终究完善拜托的事的方法(或函数)签名必须与第1步的声明1致,而且名称与第2步AddressOf后面的方法(或函数)名1致。
Private Sub DisplayMsg(ByVal mes As String) '带1个参数,且无返回类型 TextBox1.Text = mes 'DisplayMsg与AddressOf后的签名1致 End Sub
4、履行拜托
       当B与C建立联系,且完善了C的代码后,就能够调用deleg变量的Invoke方法(可带参数,应与声明1致)。
deleg.Invoke("帮我打官司") '履行拜托,参数类型与个数应与声明1致
       完全全部拜托进程以下:
Public Class frmMain '位于1个模块或类的声明部份 Private Delegate Sub OneArgSub(ByVal msg As String) '带1个参数,且无返回类型 Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click Dim deleg As OneArgSub '声明类 deleg = New OneArgSub(AddressOf DisplayMsg) '实例化1:用New deleg.Invoke("帮我打官司") '履行拜托,参数类型与个数应与声明1致 End Sub Private Sub DisplayMsg(ByVal mes As String) '带1个参数,且无返回类型 TextBox1.Text = mes 'DisplayMsg与AddressOf后的签名1致 End Sub End Class

       拜托的工作方式和所有的静态方法1样,这些静态方法是指Module中的Sub和Function进程和类中的同享进程。下面的示例使用拜托来调用类中的同享Function方法:
Public Class frmMain Private Delegate Function AskYesNoQuestion(ByVal msg As String) As Boolean Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click Dim question As AskYesNoQuestion question = New AskYesNoQuestion(AddressOf MessageDisplayer.AskYesNo) '同享方法地址返回 If question("Do you want to save?") Then '相当于调用类中的同享方法AskYesNo,这里没用invoke TextBox1.Text = "Saving......" Else TextBox1.Text = "Nothing" End If End Sub End Class Public Class MessageDisplayer Shared Function AskYesNo(ByVal mes As String) As Boolean '同享方法,通过类名调用 Dim answer As MsgBoxResult answer = MsgBox(mes, MsgBoxStyle.YesNo Or MsgBoxStyle.Question) Return (answer = MsgBoxResult.Yes) '回答是时,返回真 End Function End Class
       Invoke对system . Delegate类和所有从这个类继承得到的类来讲都是默许成员;因此,在调用这个函数时可以省略它。终究,通过拜托变量调用进程和调用方法看起来差不多:
       在使用拜托的时候,必须注意可选参数。被拜托指向的进程能够包括Optional和paramArray参数,并且拜托将正确地传递所期待的参数个数。即便目标进程被重载也会这样做,在这类情况下拜托可以正确地调用进程的重载版本。不过,拜托定义本身不能包括Optional或ParamArray参数。

2、调用实例方法
         对实例化的对象使用拜托。由于对对象的非同享方法,没法从类名访问方法的函数地址,只有通过对象实例化后,这个对象的方法的函数地址(public)才显现出来。才能使用拜托。因此AddressOf参数一定跟随的是前面实例化的对象。
Public Class frmMain Private Delegate Function AskYesNoQuestion(ByVal answer As Boolean) As Boolean Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click '首先实例化对象 Dim msgdisp As New MessageDisplayer msgdisp.MsgText = "对话类要显示的内容" msgdisp.MsgTitle = "对话类的标题 " '然后,实例化拜托 Dim question As New AskYesNoQuestion(AddressOf msgdisp.AskYesNo) '履行拜托 If question(True) Then TextBox1.Text = "回答真真的" Else TextBox1.Text = "回答是假的" End If End Sub End Class Public Class MessageDisplayer Public MsgText As String Public MsgTitle As String Function AskYesNo(ByVal DefaultAnser As Boolean) As Boolean '同享方法,通过类名调用 Dim style As MsgBoxStyle If DefaultAnser Then style = MsgBoxStyle.DefaultButton1 Else style = MsgBoxStyle.DefaultButton2 End If style = style Or MsgBoxStyle.Question Or MsgBoxStyle.YesNo Return MsgBox(MsgText, style, MsgTitle) = MsgBoxResult.Yes End Function End Class

3、拜托的属性
     所有拜托类都从System.Delegate派生,因此它们继承了在这个基类中所定义的全部属性和方法。 
    Target属性
     获得类实例,当前拜托将对其调用实例方法。它返回1个到对象的援用,这个对象是拜托的目标。如果拜托表示实例方法,则为当前拜托对其调用实例方法的对象;如果拜托表示静态方法,则为 Nothing。
      例:前面实例question.Target.ToString的结果为:Test.MessageDisplayer   (由于项目名为Test,类名为MessageDisplayer)
    如果拜托指向1个同享方法,则Target方法返回1个对代表类的System.Type对象的援用。在这类情况下,需要使用反射方法来提取与类本身有关的信息。
    Method属性
      获得拜托所表示的方法。它返回System.Reflection.Methodlnfo对象(该对象对正在被调用的方法进行说明),它的特性及其他信息。
      例:前面实例question.Method.ToString的结果为:Boolean AskYesNo(Boolean)  (这正是声明拜托的方法)

4、定义多态行动
        拜托在调用时有很大灵活性。只要所有被调用的进程应当具有相同的参数签名,即肯定调用是哪一个。例以下列各种情况:
             (1)使用拜托调用静态方法组中的1个方法:这些静态方法可以是模块中的,也能够是类中的。
             (2)使用拜托调用同1个对象实例中的不同方法。
             (3)使用拜托调用同1个类不同对象的不同方法。
             (4)使用拜托调用属于不同类的对象的不同方法。
      例:下面是1个程序履行日志记录。重点是在履行拜托事(log函数)前,log是关联到哪一个函数,可以看到1时是关联到流中,2时是关联到控制台。简言之,程序中动态的关联将致使不同的履行方式和结果。
Module Module1 Delegate Sub LogWriter(ByVal Msg As String) '声明拜托类型 Dim log As LogWriter '定义拜托变量(即上面能带1参的函数) Dim fw As System.IO.StreamWriter '定义流变量 Sub main() Dim args() As String = Environment.GetCommandLineArgs '获得命令行参数 If args.Length > 1 Then '如果命令行包括有1个文件名,打开这个文件 fw = New System.IO.StreamWriter(args(1)) log = New LogWriter(AddressOf fw.WriteLine) '1、拜托到流输出的WriteLine方法 Else log = New LogWriter(AddressOf Console.WriteLine) '2、拜托到控制台输出 End If Call DoTheRealJob() '3、子函数中才履行拜托函数 If Not (fw Is Nothing) Then fw.Close() End Sub Sub DoTheRealJob() log("Start of program.") '4、这里履行 log("In the middle of the program.") log("End of program.") End Sub End Module
    说明:Environment.GetCommandLineArgs 返回包括当前进程的命令行参数的字符串数组。这是甚么意思呢?
                这个其实与C语言最开始用main1样,其中main可带参数,也可没必要带参数,带参时:int main (int argc,char *argv[])
                第1个就是参数个数,后面就数组。main函数的参数值是从操作系统命令行上取得的。
                当我们要运行1个可履行文件时,在DOS提示符下键入文件名,再输入实际参数便可把这些实参传送到main的形参中去。比如:DOS提示符下命令行的1般情势为:
                          C:\>可履行文件名 参数 参数……;
                比如:  copy  D:\1.text   E:\3.text     (把D盘的1.text文件复制到E盘的3.text,复制中带更名)
                后面的两个就是参数,个数2个。

5、拜托和回调
--------------
        回调函数
         当程序跑起来时,1般情况下,利用程序(application program)会经常通过API调用库里所预先备好的函数。但是有些库函数(library function)却要求利用先传给它1个函数,好在适合的时候调用,以完成目标任务。这个被传入的、后又被调用的函数就称为回调函数(callback function)。

         打个比方,有1家旅馆提供叫醒服务,但是要求旅客自己决定叫醒的方法。可以是打客房电话,也能够是派服务员去敲门,睡得死怕耽误事的,还可以要求往自己头上浇盆水。这里,“叫醒”这个行动是旅馆提供的,相当于库函数,但是叫醒的方式是由旅客决定并告知旅馆的,也就是回调函数。而旅客告知旅馆怎样叫醒自己的动作,也就是把回调函数传入库函数的动作,称为登记回调函数(to register a callback function)。
-------------------
       好几种Windows API函数都使用回调机制,例如EmnuWindows和 EnumFonts,EnumWindows枚举系统中所有的顶级窗口,并为每一个査找到的窗口回调调用程序。
Declare Function EnumWindows Lib "user32" (ByVal IpEnumFunc As EnumWindows_Callback, ByVal lParam As Integer) As Integer
     拜托能够有效地用于接收来自从Windows API的回调通知,使之更安全。对上面回调声明拜托:
Delegate Function EnumWindows_Callback(ByVal hWnd As Integer, ByVal lParam As Integer) As Integer
      然后完善拜托的事(方法或函数):
Function EnumWindows_CBK(ByVal hWnd As Integer, ByVal IParam As Integer) As Integer Console.WriteLine(hWnd) '显示这个顶级窗口的句柄 Return 1 '返回1,继续罗列 End Function
      最后履行拜托便可:EnumWindows(AddressOf EnumWindows_CBK, 0)
      完全代码:(为了简化,只显示句柄)
Module Module1 Declare Function EnumWindows Lib "user32" (ByVal IpEnumFunc As EnumWindows_Callback, ByVal lParam As Integer) As Integer Delegate Function EnumWindows_Callback(ByVal hWnd As Integer, ByVal lParam As Integer) As Integer Sub main() EnumWindows(AddressOf EnumWindows_CBK, 0) Console.Read() End Sub Function EnumWindows_CBK(ByVal hWnd As Integer, ByVal IParam As Integer) As Integer Console.WriteLine(hWnd) '显示这个顶级窗口的句柄 Return 1 '返回1,继续罗列 End Function End Module

            例:下面是1个拜托中可以随时中断的例子。程序有点复杂,先不要管递归进程。只看拜托的使用及中断。
Module Module1 Delegate Function TraverseDirectoryTree_CBK(ByVal dirName As String) As Boolean Sub main() TraverseDirectoryTree("E:\", AddressOf DisplayDirectoryName) End Sub Function DisplayDirectoryName(ByVal path As String) As Boolean Console.WriteLine(path) If path = "E:\tools\Thunder\Bho" Then '3、1直查到此目录为止 Return True Else Return False End If End Function Sub TraverseDirectoryTree(ByVal path As String, ByVal callback As TraverseDirectoryTree_CBK) '1、拜托作为参数 Dim dirName As String Static nestLevel As Integer '嵌套级别 Static isCanceled As Boolean '取消罗列时为True nestLevel += 1 '嵌套层次 For Each dirName In System.IO.Directory.GetDirectories(path) isCanceled = callback.Invoke(dirName) '2、回调程序履行时返回通知 If isCanceled Then Exit For '4、通知为真(取消罗列)则退出循环 TraverseDirectoryTree(dirName, callback) '否则,递归继续罗列 Next nestLevel -= 1 '退出这个嵌套层 If nestLevel = 0 Then '如果准备返回给用户则取消重置 isCanceled = False '否则以下x次调用不能正确工作 End If End Sub End Module
      说明:1处拜托作为参数进入传入,在2处时真正调用拜托事项,3处决定拜托的状态,4处根据拜托的返回值将决定递归是不是继续。

6、多路广播拜托mulltcast
        .NET Framework支持两种类型的拜托:
        1、单播拜托
                利用单播拜托能够调用1个对象的1个方法。当在单播拜托中调用 Invoke()时,拜托播调用委派对象播的指定方法。
         2、多播拜托
                利用多播拜托能够隐式地调用不同对象的1系列方法。多播拜托支持1个调用列表,通过列表中的选项来调用哪一个对象的哪一个方法。当在多播拜托中调用Invoke时,拜托按顺序调用委派对象的指定方法。
               如果需要对1个对象集合履行相同的操作,或需要对同1个对象履行1系列操作,或是上面这两种情况的组合时,多播拜托是很有用的。利用多播拜托隐式地将需要履行的操作和履行操作所援用的对象构成1个集合。

       多播拜托的步骤.:
           (1)定义1个拜托类型:多播拜托只能够履行那些具有相同签名的方法,
           (2)编写具有相同签名的方法作为拜托.
           (3)创建拜托对象,并将拜托对象与需要通过拜托调用的第1个方法相绑定。
           (4)创建另外一个拜托对象,将其和下1个需要调用的方法相绑定。
           (5)调用System.Delegate类的Combine方法,将上面创建的两个拜托连接成1个综合的多播拜托。
                        方法Combine返回1个新的拜托,该拜托的调用列表中包括了所有的拜托。
           (6)重复履行上面两个步骤,根据实际需要创建所需的拜托,并将它们合成多播拜托。
           (7)如果需要从多播拜托中删除拜托,调用System.Delegate类的Remove方法实现。
                 Remove方法返回1个新的拜托,该拜托的调用表中不包括已删除的拜托。如果在调用列表中只包括刚刚删除的拜托,那末Remove方法返回Nothing。
           (8)当调用多播拜托所指定的方法时,只需像前面那样调用Invoke方法,便可按调用列表中的顺序调用方法,返回值为调用列表中最后1个方法的结果。

      例:主窗体不断添加子窗体,通过量播拜托同时刷新已生成的多个子窗体的背景。
       建立1个子窗体类,每次点击产生子窗体,并将其添加到多播拜托中,1旦拜托履行则多个方法(子窗体色彩)同时刷新,1旦关闭某子窗体则多播列表就移出该拜托。
     终究效果:
                         
    子窗体类(通过解决方案中右击项目,添加项->类,来完成) 

Public Class ChildForm Inherits System.Windows.Forms.Form Private Sub ChildForm_Load(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.Load Text = "Created " & DateTime.Now.ToLongTimeString() '子窗体标题 End Sub Public Function Repaint(ByVal theColor As Color) As String BackColor = theColor '子窗体背风景 Text = "Updated " & DateTime.Now.ToLongTimeString() '子窗体刷色时间 Return Me.Text End Function Protected Sub ChildForm_Cancel(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.Closing 'Tell the main form we are closing, so the main form can 'remove us from its multicast delegate Dim MyOwner As frmMain = CType(Owner, frmMain) MyOwner.ChildFormClosing(Me) End Sub End Class

       主窗体代码:
Public Class frmMain Delegate Function MyDelegate(ByVal aColor As Color) As String '1、声明拜托类 Private mTheDelegate As MyDelegate '2、定义拜托变量 Private Sub btnAddWindow_Click(sender As Object, e As EventArgs) Handles btnAddWindow.Click Dim aChildForm As New ChildForm() aChildForm.Owner = Me aChildForm.DesktopBounds = New Rectangle(800 * Rnd(), 800 * Rnd(), 300 + 200 * Rnd(), 50 + 200 * Rnd()) aChildForm.Show() '建立子窗体并显示 Dim newDelegate As MyDelegate = AddressOf aChildForm.Repaint '3、建立新拜托 If mTheDelegate Is Nothing Then '多播拜托为空 mTheDelegate = newDelegate sbStatus.Text = "Created first child form." Else mTheDelegate = System.Delegate.Combine(mTheDelegate, newDelegate) '4、不空,则添加 '显示多播拜托列表中的个数 sbStatus.Text = "Created child form " & mTheDelegate.GetInvocationList().Length & "." End If End Sub Private Sub btnColor_Click(sender As Object, e As EventArgs) Handles btnColor.Click If mTheDelegate Is Nothing Then MsgBox("多播拜托列表为空!") Exit Sub End If Dim dlgColor As New ColorDialog() dlgColor.ShowDialog() mTheDelegate.Invoke(dlgColor.Color) '6、多播拜托履行,全部刷色 sbStatus.Text = "Updated " & mTheDelegate.GetInvocationList().Length & " child form(s).” End Sub Public Sub ChildFormClosing(ByVal aChildForm As ChildForm) Dim unNeededDelegate As MyDelegate = AddressOf aChildForm.Repaint mTheDelegate = System.Delegate.Remove(mTheDelegate, unNeededDelegate) '7、关闭时,移出这个窗体的拜托 If mTheDelegate Is Nothing Then '移出后显示 sbStatus.Text = "Final child form has been closed." Else sbStatus.Text = "Child form closed, " & mTheDelegate.GetInvocationList().Length & " form(s) remaining." End If End Sub End Class
      说明:重点在主窗体代码中来认识多播拜托:
                 1处声明拜托类型(背景刷色),2处声明多播拜托变量,3处根据新子窗体添加拜托,若空,则是第1个拜托,否则在4处逐步新添拜托事项。这样就构成多个拜托事项。只要1履行(即6处)这多个已添加的就同时履行(每一个子窗体逐一更改背景),当关闭某子窗体,子窗体类,就会调用主窗体中ChildFormClosing方法,在7处完成移动该对应的列表。

------分隔线----------------------------
------分隔线----------------------------

最新技术推荐