Visual Basic - Reflection, 反映教學筆記(8) 執行動態程式碼

在 Reflection (反映)中,支援不需要提前參考就可以從組件中動態地建立物件。我們可以動態去載入一個組件,不必事先 Import ( C# 中 using ),而且在組件中執行程式碼。

這一篇的內容比較複雜,要注意的地方我都寫成註解,所以我們直接來看相關副程式。我們先來看一個最簡單,也是一般在使用組件的方式。

UseHashtable 副程式

''' <summary>
''' 使用一般 Hashtable 方法來顯示
''' </summary>
Private Sub UseHashtable()
    Console.WriteLine("產生Hashtable")
    Dim tb As New Hashtable()
    tb.Add("Hi", "Hello")
    Console.WriteLine("Hash Count: {0}", tb.Count)
    Console.WriteLine()
    Console.ReadLine()
End Sub

UseHashtable 副程式執行結果

一般方式,使用Hashtable
產生Hashtable
Hash Count: 1

以上是很一般使用 Hashtable 的方式,沒有任何特別之處。但如果我們是要透過動態程式碼的方式來撰寫,就會複雜很多。


使用動態程式碼撰寫 Hashtable 功能

動態程式碼的撰寫要比較了解物件導向,一個步驟一個頻驟建立出你想要的功能,從型別 --> 建構子 --> 方法 ...。

''' <summary>
''' 一般而言,建立動態程式碼是在開發及編譯應用程式階段時,需要載入尚未參考的程式碼。
''' 而非像下例使用的 UseDynamicHashtable 裡的 mscorlib.dll (一般應用程式都會預設載入此組件)
''' 使用動態程式碼,產生 Hashtable,取得及執行add方法、取得Count屬性。
''' 此範例複雜些,請注意註解及參考MSDN各物件說明
''' </summary>
Private Sub UseDynamicHashtable()
    Console.WriteLine("使用動態程式產生Hashtable")
    Dim path As String = "C:\Windows\Microsoft.NET\Framework\v4.0.30319\mscorlib.dll"
    Dim asm As Assembly = Assembly.LoadFile(path)
    ' 取得 Hashtable 型別, 注意下面ci取得建構式
    Console.WriteLine(" 取得Hashtable型別")
    Dim hashType As Type = asm.GetType("System.Collections.Hashtable")

    Console.WriteLine("     產生建構式")
    Dim argType() As Type = Type.EmptyTypes
    ' 用 ConstructorInfo 來建構這個新型別
    ' Type.GetConstructor(...),有三個多載,我們使用第一個來測試
    ' GetConstructor(Type()),搜尋其參數符合在指定陣列中(Type())的型別的公用執行個體建構函式。 
    ' 我們傳入一個「空的型別陣列」 argType()
    Dim ci As ConstructorInfo = hashType.GetConstructor(argType)
    ' 呼叫Invoke,產生一個空物件,即一個空的 Hashtable
    Dim newHash As Object = ci.Invoke(New Object() {})

    '' --- 這是另一種寫法 ---
    '' --- 我們傳入一個含Int32型別的陣列 ---
    'Dim argTypes() As Type = New Type() {GetType(System.Int32)}
    'Dim ci As ConstructorInfo = hashType.GetConstructor(argTypes)
    '' --- 注意,因為我們傳入的型別陣列為 Int32,所以產生物件時必須給一個 Int32 值---
    '' --- 值可以隨便給,只要是在符合 Int32 範圍即可 ---
    'Dim newHash As Object = ci.Invoke(New Object() {1024})

    Console.WriteLine("     取得Add方法")
    ' 取得型別方法
    Dim mi As MethodInfo = hashType.GetMethod("Add")
    Console.WriteLine("     執行Add方法")
    ' 執行取得的方法
    ' Public Function Invoke ( obj As Object, parameters As Object() ) As Object
    ' obj:物件,要在其上叫用方法或建構函式。
    ' parameters:叫用方法或建構函式的引數清單。
    ' 我要執行 newHash 物件(Hashtable) 的 add 方法,參數是 "Hi", "Hello"
    mi.Invoke(newHash, New Object() {"Hi", "Hello"})

    Console.WriteLine("     取得Count屬性")
    ' 取得Count屬性
    Dim pi As PropertyInfo = hashType.GetProperty("Count")
    ' Public Overridable Function GetValue ( obj As Object, index As Object() ) As Object
    ' obj:其屬性值將被傳回的物件。
    ' index:索引屬性的選擇性索引值。非索引屬性的這個值應為 Nothing。
    ' 取出 Count 值
    Dim count As Integer = CType(pi.GetValue(newHash, Nothing), Integer)

    Console.WriteLine("Hash Count: {0}", count)
    Console.WriteLine()

    Console.WriteLine("靜態成員輸出")
    ' 使用靜態成員
    Dim consoleType As Type = GetType(Console)
    ' 注意,我們傳入的型別陣列的型別是String
    Dim WriteLineMeth As MethodInfo = consoleType.GetMethod("WriteLine", New Type() {GetType(String)})
    ' 靜態成員可以不需要建立一個物件,傳入 Nothing
    ' 型別陣列的型別是String,count.ToString()
    WriteLineMeth.Invoke(Nothing, New Object() {"Hash Count:" & count.ToString()})

    ' 如果我們傳入的型別陣列的型別是Int32
    'Dim WriteLineMeth As MethodInfo = consoleType.GetMethod("WriteLine", New Type() {GetType(Int32)})
    ' 型別陣列的型別是Int32,count (Integer) 直接輸出即可
    'WriteLineMeth.Invoke(Nothing, New Object() {count})
End Sub

上面程式碼內容比較多,但看久也會發現「順序」很重要。就是 Step by Step,一、要做什麼;二、要做什麼;…依步驟把內容寫出來。所以說,我們一個封裝很好的 Framework 可以用是非常幸福的事。

UseDynamicHashtable 執行結果

動態程式碼,使用Hashtable
使用動態程式產生Hashtable
 取得Hashtable型別
     產生建構式
     取得Add方法
     執行Add方法
     取得Count屬性
Hash Count: 1

靜態成員輸出
Hash Count:1

使用動態程式碼,產生 HttpUtility 型別,取得及執行HtmlEncode、HtmlDecode方法

這裡比較難懂,看過上面 UseDynamicHashtable() 之後,我們來看個簡單的。

''' <summary>
''' 使用動態程式碼,產生 HttpUtility 型別,取得及執行HtmlEncode、HtmlDecode方法。
''' </summary>
Sub UseDynamicHttpUtility()
    Dim path As String = "C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Web.dll"
    Dim asm As Assembly = Assembly.LoadFile(path)
    ' 取得型別
    Dim utilType As Type = asm.GetType("System.Web.HttpUtility")

    ' 重點一
    ' 取得方法
    Dim encode As MethodInfo = utilType.GetMethod("HtmlEncode", New Type() {GetType(System.String)})
    Dim decode As MethodInfo = utilType.GetMethod("HtmlDecode", New Type() {GetType(System.String)})

    Dim oStr As String = "This is Bruce & Sherry's Happy Family. .."
    Console.WriteLine(oStr)

    ' 重點二
    ' 使用方法
    Dim encoded As String = CType(encode.Invoke(Nothing, New Object() {oStr}), String)
    Console.WriteLine(encoded)

    Dim decoded As String = CType(decode.Invoke(Nothing, New Object() {encoded}), String)
    Console.WriteLine(decoded)

    Console.Read()
End Sub

有沒有簡單點。

UseDynamicHttpUtility 執行結果

動態程式碼, 使用HttpUtility
This is Bruce & Sherry's Happy Family. 
This is Bruce &amp; Sherry&#39;s Happy Family. &lt;picture&gt;.
This is Bruce & Sherry's Happy Family. 

第一行是原始字串。第二行是 HtmlEncode 的結果。第三行是 HtmlDecode 的結果。

結論


簡單整理出動態程式碼有三個步驟:
  1. 取得型別
  2. 取得方法
  3. 使用方法
這一篇所需要的基礎知識比較多,算是比較難的一篇。加油!

參考資料

沒有留言:

張貼留言

感謝您的留言,如果我的文章你喜歡或對你有幫助,按個「讚」或「分享」它,我會很高興的。