VB.net學習筆記(二十三)再識委托
來源:程序員人生 發布時間:2016-06-07 15:26:14 閱讀次數:3377次
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處完成移動該對應的列表。
生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈