Visual Basic - Reflection, 反映教學筆記(2) 基礎反映了解

基礎反映了解

反映( 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(對擊看大圖)
想看什麼就看什麼,從 Source Code 我們能看光光每個組件的內容。我們先從簡單的取得 Assembly 基本資訊,然後介紹再取得 Module 基本資訊。

開發環境


  • 專案類型: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 裡提取資訊,共分五段:

  1. ShowAssemblyModuleBasicInfo(), 顯示模組基本資訊 
  2. ShowAssemblyModuleTypesInfo(), 顯示模組型別資訊 
  3. ShowAssemblyModuleMethodInfo(), 顯示模組方法資訊 
  4. ShowAssemblyModuleFieldInfo(), 顯示模組欄位資訊 
  5. 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 裡取得很多資訊。包含組件基本資料,模組基本資料,型別、方法、欄位、屬性等。

參考資料

2 則留言:

  1. 組件(Assembly)包含模組(Module),模組包含型別(Type),型別包含成員。使用反映( Reflection )可以如「動熊建立型別執行個體、繫結型別至現有物件、從現有物件取得型別…等。接著叫用型別的方法、存取屬性、存取欄位」等。

    動熊 這裡打錯字了。
    版主是用無蝦米打字的嗎?

    回覆刪除
  2. Yes, I use LIU.

    I fixed it.
    Thanks your feedback.

    回覆刪除

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