Visual Basic - Reflection, 反映教學筆記(4) 取得型別的4種方法

前面,我們已經學會了從組件(Assembly)、模組(Module)取得資訊,也試著從 AssemblyInfo.vb 取得屬性。還有那一句超重點:「組件(Assembly)包含模組(Module),模組包含型別(Type),型別包含成員。」今天我們進行第三個重點,型別(Type),進入之前,我們先看一點點 MSDN 的資料。
System.Type 類別 代表型別宣告:類別型別、介面型別、陣列型別、值型別、列舉型別、型別參數、泛型型別定義,以及開放式或封閉式的建構泛型型別。
…最下面的備註…
Type 是 System.Reflection 功能的,也是存取中繼資料 (Metadata) 的主要方法。 使用 Type 的成員以取得型別宣告的資訊,例如建構函式 (Constructor)、方法、欄位、屬性和類別的事件,以及模組和部署類別的組件。 C# typeof 運算子 (在 Visual Basic 中為 GetType 運算子,在 Visual C++ 中則為 typeid 運算子) 會傳回 Type 物件。 Type 物件表示型別是唯一的;也就是兩個 Type 物件參考只有在它們表示相同型別時才會參考相同物件。 如此就允許使用參考的相等來比較 Type 物件。…

System.Type 類別簡易介紹

透過 MSDN,我們知道,了解 Reflection 時也必須一併了解 System.Type 類別,不過 System.Type 類別是個大類別,當我看完那長長一列的屬性與方法後,頭也昏的。

System.Type 類別的屬性與方法

其實沒那麼複雜,就 Type 屬性或方法而言,基本上分成兩大類,
  1. Getxxx:Get 開頭為一類
  2. Isxxx:Is 開頭為一類
Getxxx 所代表的是「取得」,取得此型別(Type)的 xxx,從建構函式、自訂屬性、公用欄位、介面、公用成員、公用方法、公用屬性 …。反正你想得到的一切,Getxxx 都能幫你取得。

Isxxx 所代表的是「判斷」,你能拿 Isxxx 來判斷此型別(Type),例如,Type.IsArray() 判斷是否為陣列。

當我們取得型別(Type)後,我們就能夠透過 Getxxx 或 Isxxx 來取得與判斷此型別。所以,回過頭來,我們要來介紹,取得取得型別的4種方法。


取得型別的4種方法


取得型別的4種方法分別是:

  1. Assembly 取得 Type
  2. Module 取得 Type
  3. Object 取得 Type
  4. VB GetType() 取得 Type 

以下一一介紹。

Assembly 取得 Type

ShowTypeFromAssembly() 副程式,由傳入的組件需得型別資料。

''' <summary>
''' 由組件來取得型別
''' </summary>
''' <param name="asm">Assembly (組件)</param>
Sub ShowTypeFromAssembly(asm As Assembly)
    Dim asmTypes() As Type = asm.GetTypes()
    Console.WriteLine("組件取得:")
    For Each asmType In asmTypes
        Console.WriteLine("Type: {0}", asmType.Name)
    Next
    Console.WriteLine()
End Sub


我們來看主程式:

Dim d As Assembly = Assembly.GetExecutingAssembly()
ShowTypeFromAssembly(d)


我們先取得正在執行的組件,指後傳入副程式顯示型別資料。

我們得到的結果:

組件取得:
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


如果你想載入一個目錄下所有組件,那你可以參考這一篇「每周源代码19 – LINQ,多些What,少些How」很棒的文章,裡面給了一段關於 Assembly 載入很棒程式碼。

以下是我轉換為Visual Basic後的程式碼(注意,此程式碼無法執行,此為原型程式碼)

myListOfInstances = (From file In Directory.GetFiles(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "plugins"), "*.dll")
                     Let a = Assembly.LoadFrom(file)
                     From t In a.GetTypes() 
                     Where Not t.IsAbstract AndAlso t.BaseType = GetType(MyBaseClass)
                     Select CType(MyBaseClass,Activator.CreateInstance(t, credentials))).ToList()


有問題,請參考原文章。

Module 取得 Type

與 Assembly 取得 Type 不同,上面是傳入 Assembly 直接取得 Type,而在 ShowTypeFromModule() 副程式中,我們是傳入 Assembly,先由 Assembly 取得 Module 後,在從 Module 裡取得 Type。

''' <summary>
''' 由模組取得型別
''' </summary>
''' <param name="asm">Assembly (組件)</param>
Sub ShowTypeFromModule(asm As Assembly)
    Dim mods() As [Module] = asm.GetModules()
    Dim m As [Module] = mods(0)
    Dim modTypes() As Type = m.GetTypes()

    Console.WriteLine("模組取得:")
    For Each modType In modTypes
        Console.WriteLine("Type: {0}", modType.Name)
    Next
    Console.WriteLine()
End Sub


主程式和上面一樣,傳入 Assembly 的 d:

ShowTypeFromModule(d)


ShowTypeFromModule() 副程式執行結果:

模組取得:
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


從目前結果來看,從 Assembly 取得 Type = 從 Module 取得 Type。

Object 取得 Type

我們傳入物件,由物件取得Type (obj.GetType())。

''' <summary>
''' 由物件取得型別
''' </summary>
''' <param name="objArray">物件陣列</param>
Sub ShowTypeFromObject(objArray() As Object)
    For Each obj In objArray
        Dim objType As Type = obj.GetType()
        Console.WriteLine("Object取得:")
        Console.WriteLine("Type: {0}", objType.Name)
        Console.WriteLine()
        ShwoTypeAttributes(objType)
    Next
End Sub


這裡面還使用另一個副程式 ShwoTypeAttributes(),也很簡單,我們取的物件型別(ObjType)後,在從物件型別(ObjType)裡顯示相關屬性。這裡會使用到後面文章的程式碼,我先寫出來,後面文章再來解釋。我們先專注在取得型別這件事情上面。

#Region "由物件取得型別後,顯示其他資訊"

    ''' <summary>
    ''' 由物件取得型別後,額外顯示型別屬性
    ''' </summary>
    ''' <param name="objType">物件</param>
    Sub ShwoTypeAttributes(ByVal objType As Object)
        Dim type As Type = objType.GetType()
        GetTypeAttribute(type)
        ' 此為後面文章副程式,後面再來解釋
        ShowCustomAttribute(type)
        ' 此為後面文章副程式,後面再來解釋
        ShowTypeGetxxx(type)
    End Sub

    ''' <summary>
    ''' 由物件取得型別後,額外顯示相關屬性
    ''' </summary>
    ''' <param name="type">Type (型別)</param>
    Sub GetTypeAttribute(type As Type)
        Console.WriteLine("型別的屬性")
        Console.WriteLine("Assembly : {0}", type.Assembly)
        Console.WriteLine("AQN : {0}", type.AssemblyQualifiedName)
        Console.WriteLine("Attributes: {0}", type.Attributes)
        Console.WriteLine("BaseType: {0}", type.BaseType)
        Console.WriteLine("FullName: {0}", type.FullName)
        Console.WriteLine("HasElementType: {0}", type.HasElementType)
        Console.WriteLine("IsAbstract: {0}", type.IsAbstract)
        Console.WriteLine("IsByRef: {0}", type.IsByRef)
        Console.WriteLine("IsClass: {0}", type.IsClass)
        Console.WriteLine("IsEnum: {0}", type.IsEnum)
        Console.WriteLine("IsGenericType: {0}", type.IsGenericType)
        Console.WriteLine("IsInterface: {0}", type.IsInterface)
        Console.WriteLine("IsMarshalByRef: {0}", type.IsMarshalByRef)
        Console.WriteLine("IsNotPublic: {0}", type.IsNotPublic)
        Console.WriteLine("IsPrimitive: {0}", type.IsPrimitive)
        Console.WriteLine("IsPublic: {0}", type.IsPublic)
        Console.WriteLine("IsSealed: {0}", type.IsSealed)
        Console.WriteLine("IsValueType: {0}", type.IsValueType)
        Console.WriteLine("IsVisible: {0}", type.IsVisible)
        Console.WriteLine("Module: {0}", type.Module)
        Console.WriteLine("Name: {0}", type.Name)
        Console.WriteLine("Namespace: {0}", type.Namespace)
        Console.WriteLine()
        Console.ReadLine()
    End Sub
#End Region


為了讓範例能動,我先寫出來 ShowCustomAttribute(), ShowTypeGetxxx() … 相關副程式,後面文章再來解釋。我們先專注在取得型別這件事情上面。

''' <summary>
''' 由傳入參數,顯示相關型別相關屬性
''' </summary>
''' <param name="type">Type (型別)</param>
Sub ShowCustomAttribute(type As Type)
    Console.WriteLine("取得類別屬性:")
    For Each attr As Attribute In type.GetCustomAttributes(True)
        Console.WriteLine("  {0}", attr.GetType.Name)
    Next
    Console.WriteLine()
End Sub

#Region "顯示型別相關 Getxxx 方法"

    ''' <summary>
    ''' 呼叫型別相關 Getxxx 測試方法
    ''' </summary>
    ''' <param name="type">型別 (即Type) 物件</param>
    Sub ShowTypeGetxxx(type As Type)
        ShowGetProperties(type)
        ShowGetNestedTypes(type)
        ShowGetMembers(type)
        ShowGetConstructors(type)
        ShowGetEvent(type)
        ShowGetFields(type)
        ShowGetMethods(type)
    End Sub

    ''' <summary>
    ''' 簡單測試及顯示型別的屬性(Properties)
    ''' </summary>
    ''' <param name="type">型別 (即Type) 物件</param>
    Private Sub ShowGetProperties(type As Type)
        Console.WriteLine("取得型別Properties")
        For Each pi As PropertyInfo In type.GetProperties
            Console.WriteLine("{0}", pi.Name)
        Next
        Console.WriteLine()
        Console.ReadLine()
    End Sub

    ''' <summary>
    ''' 簡單測試及顯示型別的NestedTypes
    ''' </summary>
    ''' <param name="type">型別 (即Type) 物件</param>
    Private Sub ShowGetNestedTypes(type As Type)
        Console.WriteLine("取得型別NestedTypes")
        For Each gnt As Type In type.GetNestedTypes
            Console.WriteLine("{0}", gnt.Name)
        Next
        Console.WriteLine()
        Console.ReadLine()
    End Sub

    ''' <summary>
    ''' 簡單測試及顯示型別 GetMembers() 方法
    ''' </summary>
    ''' <param name="type">型別 (即Type) 物件</param>
    Private Sub ShowGetMembers(type As Type)
        Console.WriteLine("取得型別Members")
        For Each mi As MemberInfo In type.GetMembers()
            Console.WriteLine("    Member的 Type 及 名稱")
            Console.WriteLine("          {0}:{1}", mi.MemberType, mi.Name)
            ShowMemberType(mi)
        Next
        Console.WriteLine()
        Console.ReadLine()
    End Sub

    ''' <summary>
    ''' 簡單測試及顯示MemberInfo裡MemberType成員,目前只讓 ShowGetMembers 呼叫
    ''' </summary>
    ''' <param name="mi">MemberInfo物件</param>
    Private Sub ShowMemberType(mi As MemberInfo)
        ' MemberInfo 可藉由 MemberTypes 來判斷出型別的不同成員,注意,都是public。
        ' 簡單測試 MemberTypes.Property
        Select Case mi.MemberType
            Case MemberTypes.Constructor
            Case MemberTypes.Custom
            Case MemberTypes.Event
            Case MemberTypes.Field
            Case MemberTypes.Method
            Case MemberTypes.NestedType
            Case MemberTypes.Property
                Dim pi As PropertyInfo = CType(mi, PropertyInfo)
                Console.WriteLine("          Property Type: {0}", pi.PropertyType.Name)
            Case MemberTypes.TypeInfo
            Case MemberTypes.All
            Case Else
                Console.WriteLine("什麼!完全沒有符合的成員!")
        End Select
    End Sub

    ''' <summary>
    ''' 簡單測試及顯示型別 GetConstructors 方法
    ''' </summary>
    ''' <param name="type">型別 (即Type) 物件</param>
    Sub ShowGetConstructors(type As Type)
        Console.WriteLine("取得型別Constructors")
        For Each ci As ConstructorInfo In type.GetConstructors
            Console.WriteLine("    IsSecuritySafeCritical: {0}", ci.IsSecuritySafeCritical)
        Next
        Console.WriteLine()
        Console.ReadLine()
    End Sub

    ''' <summary>
    ''' 簡單測試及顯示型別 GetEvents 方法
    ''' </summary>
    ''' <param name="type">型別 (即Type) 物件</param>
    Sub ShowGetEvent(type As Type)
        Console.WriteLine("取得型別Events")
        For Each ei As EventInfo In type.GetEvents
            Console.WriteLine("    DeclaringType: {0}", ei.DeclaringType)
        Next
        Console.WriteLine()
        Console.ReadLine()
    End Sub

    ''' <summary>
    ''' 簡單測試及顯示型別 GetFields 方法
    ''' </summary>
    ''' <param name="type">型別 (即Type) 物件</param>
    Sub ShowGetFields(type As Type)
        Console.WriteLine("取得型別Fields")
        For Each fi As FieldInfo In type.GetFields
            Console.WriteLine("    FieldType: {0}", fi.FieldType)
        Next
        Console.WriteLine()
        Console.ReadLine()
    End Sub

    ''' <summary>
    ''' 簡單測試及顯示型別 GetMethods 方法
    ''' </summary>
    ''' <param name="type">型別 (即Type) 物件</param>
    Sub ShowGetMethods(type As Type)
        Console.WriteLine("取得型別Methods")
        For Each mi As MethodInfo In type.GetMethods
            Console.WriteLine("Method: {0}, IsPublic: {1}", mi.GetBaseDefinition.Name, mi.IsPublic)
        Next
        Console.WriteLine()
        Console.ReadLine()
    End Sub

#End Region


主程式與前二個不同,我們必須傳入一個物件陣列,這個陣列的內容你可以自訂。我傳入四個 Object,分別為 String, Int32, Double, Boolean。

Dim t() As Object = {"String", 1, 0.5, True}
ShowTypeFromObject(t) ' String, Int32, Double, Boolean


我們來看執行結果,由於結果很長,我只先貼上 String 的結果。

Object取得:
Type: String

型別的屬性
Assembly : mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
AQN : System.RuntimeType, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
Attributes: AutoLayout, AnsiClass, Class, Serializable, BeforeFieldInit
BaseType: System.Type
FullName: System.RuntimeType
HasElementType: False
IsAbstract: False
IsByRef: False
IsClass: True
IsEnum: False
IsGenericType: False
IsInterface: False
IsMarshalByRef: False
IsNotPublic: True
IsPrimitive: False
IsPublic: False
IsSealed: False
IsValueType: False
IsVisible: False
Module: CommonLanguageRuntimeLibrary
Name: RuntimeType
Namespace: System

取得類別屬性:
  SerializableAttribute

取得型別Properties
Module
Assembly
TypeHandle
DeclaringMethod
BaseType
UnderlyingSystemType
FullName
...

取得型別NestedTypes

    Member的 Type 及 名稱
          Method:GetMethod
    Member的 Type 及 名稱
          Method:GetMethod
    Member的 Type 及 名稱
          Method:GetMethods
    Member的 Type 及 名稱
          Method:GetField
    Member的 Type 及 名稱
          Method:GetFields
...

    Member的 Type 及 名稱
          Property:Module
          Property Type: Module
    Member的 Type 及 名稱
          Property:Assembly
          Property Type: Assembly
    Member的 Type 及 名稱
          Property:TypeHandle
          Property Type: RuntimeTypeHandle
    Member的 Type 及 名稱
          Property:DeclaringMethod
          Property Type: MethodBase
...

取得型別Constructors


取得型別Events


取得型別Fields


取得型別Methods
Method: get_Module, IsPublic: True
Method: get_Assembly, IsPublic: True
Method: get_TypeHandle, IsPublic: True
Method: get_DeclaringMethod, IsPublic: True
Method: get_BaseType, IsPublic: True
...


這裡先不急,相關副程式我們後面的慢慢說,但從這裡我們也知道一件事,取得型別(Type)後,我們還能再往下進行很多事,了解這些層級關係會對我們學習反映( Reflection )很有幫助。

VB GetType() 取得 Type

相較於前面的範例,使用VB本身的GetType()來取得Type(型別),是比較簡單的。

''' <summary>
''' 由VB本身的 GetType() 來取得型別
''' </summary>
Sub ShowTypeFromVBGetType()
    Dim vbtype As Type = GetType(Int32)
    Console.WriteLine("VB GetType取得:")
    Console.WriteLine("Type: {0}", vbtype.FullName)
    Console.WriteLine()
End Sub


這個 ShowTypeFromVBGetType() 副程式比較沒有彈性,GetType(Int32) 我是寫死的。沒有傳入參考再依參數來決定來取得Type,不過上面寫太多了,有點累了,這就交給你們自行修改。主程式直接呼叫 ShowTypeFromVBGetType() 使用即可。

我們來看執行結果:

VB GetType取得:
Type: System.Int32

如果你想看其他結果,你使能用 Object 取得 Type 裡的副程式,這裡我只是最簡易的顯示一個 FullName。

結論


以上我們就「取得型別(Type)」這件事,進行很長的討論。取得了型別後,接下來,我們就可以做很多事。

參考資料


沒有留言:

張貼留言

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