基礎反映了解
反映( Reflection )主要是用於檢測型別,讓你可以取得已載入的組件(*.dll)及組件中所定義型別的相關資料。
組件(Assembly)包含模組(Module),模組包含型別(Type),型別包含成員。使用反映( Reflection )可以如「動態建立型別執行個體、繫結型別至現有物件、從現有物件取得型別…等。接著叫用型別的方法、存取屬性、存取欄位」等。
舉例,如果我們開發一個 Mvc3GuestbookVB 網站,編譯後,會在 網站 \bin 目錄下產生相關的 DLL組件檔案,
圖一、Mvc 網站編譯輯產生的組件(對擊看大圖) |
裡面的 *.dll 都是組件,組件包含了所有執行的必要資訊,由於.NET Framework是在 CLR 環境執行,所以我們都能透過 Reflection 去看組件中所有資訊。例如,我們使用 Telerik JustDecompile (目前免費) 解析工具,去解析其中的 Mvc3GuestbookVB04.dll 組件。
圖二:使用 Telerik JustDecomplile 解析組件(對擊看大圖) |
又例如,我們這個學習Reflection的範例,是寫成「Console 主控台」專案,編譯後是一個 AssemblyDemo.exe 執行檔,一樣可以使用 Reflection 去取得相關資訊,例如,
圖三:解析 Console AssemblyDemo.exe(對擊看大圖) |
那我寫的.NET Framework程式碼不就被看光光?基本上,是的。沒辦法,這是 Microsoft Intermediate Language (MSIL) 的特性。所以市面上有賣「程式干擾器」,類似 Google Closure Compiler原理,把程式搞的亂七八糟,但是能正常執行,當我去 Reflection 時,增加"人"閱讀的難度。不過前提是,他要能拿得到你那編譯好的組件檔。不過就我了解,價格都不太便宜。
我去聽課時,一些講師很喜歡用類似 Telerik JustDecompile 工具去看 .NET Framework 裡 DLL 的Source Code,那時候覺得他們好神!聽他們說,這樣能更了解更底層的內容,也間接幫助記憶與了解此類別,因為有時連 MSDN 都交代的不清不楚。例如,我們想更了解 Entity Framework 4.1的內容,那我們就可以去下載 EntityFramework.dll,
圖四:解析 EntityFramework 4.1 DbContext(對擊看大圖) |
開發環境
- 專案類型:Visual Basic - 主控台應用程式
- 專案名稱:AssemblyDemo
- 開發工具:Visual Studio 2010
- .NET 版本:.NET Framework 4
基本上,在 Visual Studio 2008 / .NET Framework 2,也是能運作,只不過我最後一篇文章會附上一個完整範例檔 (共兩個版本,一個基礎版,一個重構為OO版),先說明我的環境。
顯示 Assembly 基本資訊
#Region "顯示組件基本資訊" #Region "Sample 1,2" ''' <summary> ''' 顯示 Assembly 基本資訊 ''' </summary> ''' <param name="asm">組件(Assembly)</param> Sub ShowAssemblyInfo(asm As Assembly) Console.WriteLine("--- Assembly Information ---") Console.WriteLine("FullName: {0}", asm.FullName) Console.WriteLine("From GAC: {0}", asm.GlobalAssemblyCache) Console.WriteLine("Path: {0}", asm.Location) Console.WriteLine("Only Reflection: {0}", asm.ReflectionOnly) Console.WriteLine("Version: {0}", asm.ImageRuntimeVersion) Console.WriteLine("--- 按 Ennter 繼續 ---") Console.ReadLine() End Sub #End Region #End Region #Region "顯示模組相關資訊函式" #Region "Sample 1,2" ''' <summary> ''' 顯示模組相關資訊 ''' </summary> ''' <param name="asm">組件(Assembly)</param> Sub ShowAssemblyModuleInfo(asm As Assembly) Dim mods() As [Module] = asm.GetModules For Each md As [Module] In mods ShowAssemblyModuleBasicInfo(md) ShowAssemblyModuleTypesInfo(md) ShowAssemblyModuleMethodInfo(md) ShowAssemblyModuleFieldInfo(md) ShowAssemblyModuleAttributeInfo(md) Console.WriteLine("--- 按 Ennter 繼續 ---") Console.ReadLine() Next End Sub ''' <summary> ''' 顯示模組基本資訊 ''' </summary> ''' <param name="md">模組(Module)</param> Private Sub ShowAssemblyModuleBasicInfo(md As [Module]) Console.WriteLine("--- Basic Information ---") Console.WriteLine("Name: {0}", md.Name) Console.WriteLine("Assembly: {0}", md.Assembly) Console.WriteLine("FQN: {0}", md.FullyQualifiedName) Console.WriteLine("--- 按 Ennter 繼續 ---") Console.ReadLine() End Sub ''' <summary> ''' 顯示模組型別資訊 ''' </summary> ''' <param name="md">模組(Module)</param> Private Sub ShowAssemblyModuleTypesInfo(md As [Module]) Console.WriteLine("--- Types Information ---") Dim Types() As Type = md.GetTypes() For Each type As Type In Types Console.WriteLine("Type: {0}", type.Name) Next Console.WriteLine("--- 按 Ennter 繼續 ---") Console.ReadLine() End Sub ''' <summary> ''' 顯示模組方法資訊 ''' </summary> ''' <param name="md">模組(Module)</param> Private Sub ShowAssemblyModuleMethodInfo(md As [Module]) Console.WriteLine("--- Method Information") Dim methods() As MethodInfo = md.GetMethods() For Each mi As MethodInfo In methods Console.WriteLine("Method: {0}", mi.Name) Next Console.WriteLine("--- 按 Ennter 繼續 ---") Console.ReadLine() End Sub ''' <summary> ''' 顯示模組欄位資訊 ''' </summary> ''' <param name="md">模組(Module)</param> Private Sub ShowAssemblyModuleFieldInfo(md As [Module]) Console.WriteLine("--- Field Information") Dim fields() As FieldInfo = md.GetFields() For Each fi As FieldInfo In fields Console.WriteLine("Field: {0}", fi.Name) Next Console.WriteLine("--- 按 Ennter 繼續 ---") Console.ReadLine() End Sub ''' <summary> ''' 顯示模組屬性資訊 ''' </summary> ''' <param name="md">模組(Module)</param> Private Sub ShowAssemblyModuleAttributeInfo(md As [Module]) Console.WriteLine("--- Attribute Information ---") Dim gcas() As Object = md.GetCustomAttributes(False) For Each gca As Object In gcas Console.WriteLine("GetCustomAttribute: {0}", gca.GetType.Name) Next Console.WriteLine("--- 按 Ennter 繼續 ---") Console.ReadLine() End Sub #End Region #End Region
第二次,重覆一下這句話:「組件(Assembly)包含模組(Module),模組包含型別(Type),型別包含成員。」,或換個角度來想,一個Class (類別)裡有什麼東西?
接下來在 Module 執行副程式。
Imports System.Reflection Imports System.Reflection.Emit Module Module1 Sub main() ' Sample 1: 載入特定Assembly Dim path As String = "C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.dll" Dim a As Assembly = Assembly.LoadFile(path) ShowAssemblyInfo(a) ShowAssemblyModuleInfo(a) ' Sample 2: 取得現在執行Assembly Dim b As Assembly = Assembly.GetExecutingAssembly() ShowAssemblyInfo(b) ShowAssemblyModuleInfo(b) End Sub ' 相關副程式(Sub)放這裡 End Module
一個Class從上而下可以有:建構子、成員、屬性、副程式(函式)。如果你 OOP 不熟的話,可以參考一下我寫的 VB OOP 相關文章。只不過,我們從 Reflection 裡可以了解到更詳細的資料,例如,執行 Sample 1 的 ShowAssemblyInfo() 副程式:
--- Assembly Information --- FullName: System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 From GAC: True Path: C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System\v4.0_4.0.0.0__b77a5c561934e089\System.dll Only Reflection: False Version: v4.0.30319 --- 按 Ennter 繼續 ---
我們能得到此組件的 FullName, 是否從GAC來,它的路徑,是否只Reflection,組件版本 …等基本資訊。接下來,我們執行 ShowAssemblyModuleInfo() 副程式是從 Module 裡提取資訊,共分五段:
- ShowAssemblyModuleBasicInfo(), 顯示模組基本資訊
- ShowAssemblyModuleTypesInfo(), 顯示模組型別資訊
- ShowAssemblyModuleMethodInfo(), 顯示模組方法資訊
- ShowAssemblyModuleFieldInfo(), 顯示模組欄位資訊
- ShowAssemblyModuleAttributeInfo(), 顯示模組屬性資訊
ShowAssemblyModuleBasicInfo() 結果:
--- Basic Information --- Name: System.dll Assembly: System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 FQN: C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System\v4.0_4.0.0.0__b77a5c561934e089\System.dll --- 按 Ennter 繼續 ---
ShowAssemblyModuleTypesInfo() 結果:
…以上略… Type: DataStopCommand Type: EHelloCommand Type: HelloCommand Type: StartTlsCommand Type: MailCommand …以下略…
ShowAssemblyModuleMethodInfo(), ShowAssemblyModuleFieldInfo() 無資訊。
ShowAssemblyModuleAttributeInfo() 結果:
--- Attribute Information --- GetCustomAttribute: UnverifiableCodeAttribute --- 按 Ennter 繼續 ---
以上取的內容,有沒有很相似的感覺,只是比我們自己寫Class時更詳細的內容。我們再來看,Sample 2 的執行結果,我們要取得現在正在執行的組件資訊。
ShowAssemblyInfo() 結果:
--- Assembly Information --- FullName: AssemblyDemo, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null From GAC: False Path: D:\StudyTest\...\AssemblyDemo\bin\Debug\AssemblyDemo.exe Only Reflection: False Version: v4.0.30319 --- 按 Ennter 繼續 ---
注意 FullName 及 GAC 及 Path 這三項資訊。
ShowAssemblyModuleBasicInfo() 結果:
--- Basic Information --- Name: AssemblyDemo.exe Assembly: AssemblyDemo, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null FQN: D:\StudyTest\...\AssemblyDemo\bin\Debug\AssemblyDemo.exe --- 按 Ennter 繼續 ---
ShowAssemblyModuleTypesInfo() 結果:
--- Types Information --- Type: MyApplication Type: MyComputer Type: MyProject Type: MyWebServices Type: ThreadSafeObjectProvider`1 Type: InternalXmlHelper Type: RemoveNamespaceAttributesClosure Type: Module1 Type: Example Type: Resources Type: MySettings Type: MySettingsProperty --- 按 Ennter 繼續 ---
Module1是我們 Console 主程式本身,Example 是從 MSDN Copy 而來的一個 Class,在後面範例會使用到。
ShowAssemblyModuleMethodInfo()、ShowAssemblyModuleFieldInfo()、ShowAssemblyModuleAttributeInfo() 無資訊。
第三次,重覆一下這句話:「組件(Assembly)包含模組(Module),模組包含型別(Type),型別包含成員。」以上,我們學習到可以從 Assembly 裡取得一些資訊,也能從 Module 裡取得很多資訊。包含組件基本資料,模組基本資料,型別、方法、欄位、屬性等。
組件(Assembly)包含模組(Module),模組包含型別(Type),型別包含成員。使用反映( Reflection )可以如「動熊建立型別執行個體、繫結型別至現有物件、從現有物件取得型別…等。接著叫用型別的方法、存取屬性、存取欄位」等。
回覆刪除動熊 這裡打錯字了。
版主是用無蝦米打字的嗎?
Yes, I use LIU.
回覆刪除I fixed it.
Thanks your feedback.