一樣,應該要注意的內容,我都註解到程式碼中,與前一篇「Visual Basic - Reflection, 反映教學筆記(8) 執行動態程式碼」很像,就是 Step by Step 的建立出你的組件。重點在於我們是使用 System.Reflection.Emit 類別裡 *Builder 結尾的類別,來動態建立出我們的組件。
*Builder 結尾的類別類似觀念,我們前面「」就已經談過,觀念一樣,我就不在重覆。你要建立什麼就是 ...Builder 類別。記的要先Imports System.Reflection.Emit。
RunTimeCreateAssembly 副程式
#Region "執行環境中產生程式碼"
''' <summary>
''' 建立動態組件
''' </summary>
Private Sub RunTimeCreateAssembly()
' 1. 建立組件
Console.WriteLine("定義組件")
Dim tempName As New AssemblyName()
tempName.Name = "KKBruceSampleAssembly"
tempName.Version = New Version("1.0.0.0")
' 使用 DefineDynamicAssembly 動態產生組件
Dim AssemBL As AssemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(tempName, AssemblyBuilderAccess.RunAndSave)
' 2. 建立模組
Console.WriteLine("定義模組")
' DefineDynamicModule 動態產生模組
' 如果要建立一個單一檔案的組件,並將此組件序列化在磁碟之中,最後的Save名稱必須與以下DefineDynamicModule裡名稱相同。
Dim ModuleBL As ModuleBuilder = AssemBL.DefineDynamicModule(tempName.Name, tempName.Name & ".dll")
' 3. 建立型別
Console.WriteLine("定義型別")
' DefineType 動態產生型別
Dim BruceType As TypeBuilder = ModuleBL.DefineType("KKBruceType", TypeAttributes.Class Or TypeAttributes.Public)
' 指定父類別及介面給DefineType
'Dim tb As TypeBuilder = mb.DefineType("KKBruceMyNewType", TypeAttributes.Class Or TypeAttributes.Public,
' GetType(Hashtable),
' New Type() {GetType(IDisposable)})
' 4. 建立成員
Console.WriteLine("定義相關成員")
' 4.1 建立建構子
Console.WriteLine(" 定義建構子")
' MethodAttributes代表建構子的類型
'Dim ConstructorBL As ConstructorBuilder = BruceType.DefineDefaultConstructor(MethodAttributes.Public)
' 4.2 利用ConstructorBuilder建立ILGenerator物件
' --- 註:執行會產生錯誤 ---
' --- 註:依MSDN查詢:Runtime 會為預設建構函式產生程式碼。因此,如果嘗試取得 ILGenerator,則會擲回例外狀況。 ---
'Console.WriteLine(" 建立ILGenerator物件")
' ILGenerator 可產生 Microsoft Intermediate Language (MSIL) 指令。
'Dim codeGen As ILGenerator = ConstructorBL.GetILGenerator()
' Emit:多載。放置指令到 Just-In-Time (JIT) 編譯器的 Microsoft Intermediate Language (MSIL) 資料流中。
' OpCodes 類別:提供 Microsoft Intermediate Language (MSIL) 指令的欄位表示,以用於 ILGenerator 類別成員 (例如 Emit) 的發出。
' Ret欄位:從目前方法傳回,將被呼叫端評估堆疊的傳回值 (如果有的話) 推入至呼叫端的評估堆疊。
'codeGen.Emit(OpCodes.Ret)
' 4.3 建立方法
Console.WriteLine(" 定義方法")
Dim MethodBLOne As MethodBuilder = BruceType.DefineMethod("AddStringOne", MethodAttributes.Public, Nothing, New Type() {GetType(String)})
' 第三個參數:建新新方法的回傳型別
' 第四個參數:參數型別
Dim MethodBLTwo As MethodBuilder = BruceType.DefineMethod("AddStringTwo", MethodAttributes.Public Or MethodAttributes.Static,
Nothing,
New Type() {GetType(String)})
' 4.4 建立私有欄位
Console.WriteLine(" 定義私有欄位")
Dim FieldBL As FieldBuilder = BruceType.DefineField("_count", GetType(Int32), FieldAttributes.Private)
' 4.5 建立屬性
Console.WriteLine(" 定義屬性")
' PropertyAttributes.None 列舉常數不能定義所有的屬性
Dim PropertyBL As PropertyBuilder = BruceType.DefineProperty("Count", PropertyAttributes.None, GetType(Int32), Type.EmptyTypes)
' 4.6 在方法中定義設得或設定屬性值
Console.WriteLine(" 定義方法中設得或設定屬性值")
' 注意,使用MethodAttributes
Dim getMethodAttributes As MethodAttributes = MethodAttributes.Public Or
MethodAttributes.SpecialName Or
MethodAttributes.HideBySig
Dim propGet As MethodBuilder = BruceType.DefineMethod("get_Count", getMethodAttributes, GetType(Int32), Type.EmptyTypes)
' 讓方法可以設定與取得屬性值
PropertyBL.SetGetMethod(propGet)
' 5. 建立型別
Console.WriteLine("產生新型別")
' --- 註:建立型別之前要為所有 MethodBuilder 執行 GetILGenerator() ---
' --- 註:不然會產生一個「方法 'xxx' 沒有方法主體。」的錯誤,這裡 Debug 花了我好久!XD ---
' --- 註:參考:http://msdn.microsoft.com/zh-tw/library/system.reflection.emit.methodbuilder.definegenericparameters%28VS.85%29.aspx 範例才找出解法 ---
Console.WriteLine(" 產生所有方法的ILGenerator")
RunMethodBLGetILGenerator(MethodBLOne)
RunMethodBLGetILGenerator(MethodBLTwo)
RunMethodBLGetILGenerator(propGet)
Console.Write(" 產生型別")
Dim KKBruceType As Type = BruceType.CreateType()
' 5.1 顯示此型別方法
Console.WriteLine()
Console.WriteLine(" 顯示型別方法(注意我們產生動態產生的方法)")
GetRunTimeAssemblyMethods(KKBruceType)
Console.WriteLine()
Console.ReadLine()
' 6. 儲存組件到磁碟中
Console.WriteLine("儲存組件到磁碟中...")
' 一但被寫入到磁碟中,任何程式都可以載入這個組件使用。
AssemBL.Save(tempName.Name & ".dll")
Console.WriteLine("儲存完成。(請參考專案目錄下「\bin\Debug」產生的動態組件 " & tempName.Name & ".dll")
Console.ReadLine()
End Sub
''' <summary>
''' 傳回這個方法的 ILGenerator
''' </summary>
''' <param name="methodInstance">MethodBuilder 的執行個體</param>
''' <remarks></remarks>
Private Sub RunMethodBLGetILGenerator(methodInstance As MethodBuilder)
' MethodBuilder.GetILGenerator()
' 傳回這個方法的 ILGenerator,使用預設 Microsoft Intermediate Language (MSIL) 資料流的 64 位元大小。
Dim ilgen As ILGenerator = methodInstance.GetILGenerator()
ilgen.Emit(OpCodes.Ldnull)
ilgen.Emit(OpCodes.Ret)
End Sub
''' <summary>
''' 取得動態組件方法屬性
''' </summary>
''' <param name="KKBruceType">型別</param>
Private Sub GetRunTimeAssemblyMethods(ByVal KKBruceType As Type)
Console.WriteLine(" Full Name: {0}", KKBruceType.FullName)
For Each m As MemberInfo In KKBruceType.GetMethods()
Console.WriteLine(" Member({0}):{1}", m.MemberType, m.Name)
Next
End Sub
#End Region
執行結果
執行環境中產生程式碼
定義組件
定義模組
定義型別
定義相關成員
定義建構子
定義方法
定義私有欄位
定義屬性
定義方法中設得或設定屬性值
產生新型別
產生所有方法的ILGenerator
產生型別
顯示型別方法(注意我們產生動態產生的方法)
Full Name: KKBruceType
Member(Method):AddStringOne
Member(Method):AddStringTwo
Member(Method):get_Count
Member(Method):ToString
Member(Method):Equals
Member(Method):GetHashCode
Member(Method):GetType
儲存組件到磁碟中...
儲存完成。(請參考專案目錄下「\bin\Debug」產生的動態組件 KKBruceSampleAssembly.dll
注意我們自行新增的 Method (AddStringOne, AddStringTwo, get_Count),確認一下產生的 DLL 檔:
![]() |
| 圖一:儲存動態程式碼為組件 |
參考資料
- System.Reflection.Emit 類別
- MethodBuilder.DefineGenericParameters 方法
- Visual Basic - Reflection, 反映教學筆記(1) 前言
- Visual Basic - Reflection, 反映教學筆記(2) 基礎反映了解
- Visual Basic - Reflection, 反映教學筆記(3) 取得特定屬性
- Visual Basic - Reflection, 反映教學筆記(4) 取得型別的4種方法
- Visual Basic - Reflection, 反映教學筆記(5) 取得型別相關屬性
- Visual Basic - Reflection, 反映教學筆記(6) 暸解MethodBody
- Visual Basic - Reflection, 反映教學筆記(7) - BindingFlags 過濾 Member(成員)
- Visual Basic - Reflection, 反映教學筆記(8) 執行動態程式碼
下載 Reflection (1) ~ (9) 範例程式碼
感謝你們看到這九篇文章,我將第一篇至第九篇的範例整理成一個小小小小的 Console 選擇遊戲,以下是原始碼專案,必須使用 Visual Studio 2010 / .NET Framework 4 才能重新編譯。已編譯執行檔在 \bin\Debug\AssemblyDemo.exe,如果你想試試我前面介紹的 Reflection 工具,也可以從已編譯執行檔裡去看原始碼。解壓縮密碼:KKBruce
此範例程式碼是「太濕版」一點也不「DRY, 乾」,全部用副程式在呼叫,下一篇我會將把此範例改寫為 OO (物件導向, Object-oriented) 的範例,算是大重構,不過從其中我們也能看到物件導向的優點,很多事你硬要使用「副程式/函式」來做是會…濕透了。

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