基礎反映了解
反映( 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.