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() 副程式,由傳入的組件需得型別資料。

01''' <summary>
02''' 由組件來取得型別
03''' </summary>
04''' <param name="asm">Assembly (組件)</param>
05Sub ShowTypeFromAssembly(asm As Assembly)
06    Dim asmTypes() As Type = asm.GetTypes()
07    Console.WriteLine("組件取得:")
08    For Each asmType In asmTypes
09        Console.WriteLine("Type: {0}", asmType.Name)
10    Next
11    Console.WriteLine()
12End Sub


我們來看主程式:

1Dim d As Assembly = Assembly.GetExecutingAssembly()
2ShowTypeFromAssembly(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後的程式碼(注意,此程式碼無法執行,此為原型程式碼)

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


有問題,請參考原文章。

Module 取得 Type

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

01''' <summary>
02''' 由模組取得型別
03''' </summary>
04''' <param name="asm">Assembly (組件)</param>
05Sub ShowTypeFromModule(asm As Assembly)
06    Dim mods() As [Module] = asm.GetModules()
07    Dim m As [Module] = mods(0)
08    Dim modTypes() As Type = m.GetTypes()
09 
10    Console.WriteLine("模組取得:")
11    For Each modType In modTypes
12        Console.WriteLine("Type: {0}", modType.Name)
13    Next
14    Console.WriteLine()
15End Sub


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

1ShowTypeFromModule(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())。

01''' <summary>
02''' 由物件取得型別
03''' </summary>
04''' <param name="objArray">物件陣列</param>
05Sub ShowTypeFromObject(objArray() As Object)
06    For Each obj In objArray
07        Dim objType As Type = obj.GetType()
08        Console.WriteLine("Object取得:")
09        Console.WriteLine("Type: {0}", objType.Name)
10        Console.WriteLine()
11        ShwoTypeAttributes(objType)
12    Next
13End Sub


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

01#Region "由物件取得型別後,顯示其他資訊"
02 
03    ''' <summary>
04    ''' 由物件取得型別後,額外顯示型別屬性
05    ''' </summary>
06    ''' <param name="objType">物件</param>
07    Sub ShwoTypeAttributes(ByVal objType As Object)
08        Dim type As Type = objType.GetType()
09        GetTypeAttribute(type)
10        ' 此為後面文章副程式,後面再來解釋
11        ShowCustomAttribute(type)
12        ' 此為後面文章副程式,後面再來解釋
13        ShowTypeGetxxx(type)
14    End Sub
15 
16    ''' <summary>
17    ''' 由物件取得型別後,額外顯示相關屬性
18    ''' </summary>
19    ''' <param name="type">Type (型別)</param>
20    Sub GetTypeAttribute(type As Type)
21        Console.WriteLine("型別的屬性")
22        Console.WriteLine("Assembly : {0}", type.Assembly)
23        Console.WriteLine("AQN : {0}", type.AssemblyQualifiedName)
24        Console.WriteLine("Attributes: {0}", type.Attributes)
25        Console.WriteLine("BaseType: {0}", type.BaseType)
26        Console.WriteLine("FullName: {0}", type.FullName)
27        Console.WriteLine("HasElementType: {0}", type.HasElementType)
28        Console.WriteLine("IsAbstract: {0}", type.IsAbstract)
29        Console.WriteLine("IsByRef: {0}", type.IsByRef)
30        Console.WriteLine("IsClass: {0}", type.IsClass)
31        Console.WriteLine("IsEnum: {0}", type.IsEnum)
32        Console.WriteLine("IsGenericType: {0}", type.IsGenericType)
33        Console.WriteLine("IsInterface: {0}", type.IsInterface)
34        Console.WriteLine("IsMarshalByRef: {0}", type.IsMarshalByRef)
35        Console.WriteLine("IsNotPublic: {0}", type.IsNotPublic)
36        Console.WriteLine("IsPrimitive: {0}", type.IsPrimitive)
37        Console.WriteLine("IsPublic: {0}", type.IsPublic)
38        Console.WriteLine("IsSealed: {0}", type.IsSealed)
39        Console.WriteLine("IsValueType: {0}", type.IsValueType)
40        Console.WriteLine("IsVisible: {0}", type.IsVisible)
41        Console.WriteLine("Module: {0}", type.Module)
42        Console.WriteLine("Name: {0}", type.Name)
43        Console.WriteLine("Namespace: {0}", type.Namespace)
44        Console.WriteLine()
45        Console.ReadLine()
46    End Sub
47#End Region


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

001''' <summary>
002''' 由傳入參數,顯示相關型別相關屬性
003''' </summary>
004''' <param name="type">Type (型別)</param>
005Sub ShowCustomAttribute(type As Type)
006    Console.WriteLine("取得類別屬性:")
007    For Each attr As Attribute In type.GetCustomAttributes(True)
008        Console.WriteLine("  {0}", attr.GetType.Name)
009    Next
010    Console.WriteLine()
011End Sub
012 
013#Region "顯示型別相關 Getxxx 方法"
014 
015    ''' <summary>
016    ''' 呼叫型別相關 Getxxx 測試方法
017    ''' </summary>
018    ''' <param name="type">型別 (即Type) 物件</param>
019    Sub ShowTypeGetxxx(type As Type)
020        ShowGetProperties(type)
021        ShowGetNestedTypes(type)
022        ShowGetMembers(type)
023        ShowGetConstructors(type)
024        ShowGetEvent(type)
025        ShowGetFields(type)
026        ShowGetMethods(type)
027    End Sub
028 
029    ''' <summary>
030    ''' 簡單測試及顯示型別的屬性(Properties)
031    ''' </summary>
032    ''' <param name="type">型別 (即Type) 物件</param>
033    Private Sub ShowGetProperties(type As Type)
034        Console.WriteLine("取得型別Properties")
035        For Each pi As PropertyInfo In type.GetProperties
036            Console.WriteLine("{0}", pi.Name)
037        Next
038        Console.WriteLine()
039        Console.ReadLine()
040    End Sub
041 
042    ''' <summary>
043    ''' 簡單測試及顯示型別的NestedTypes
044    ''' </summary>
045    ''' <param name="type">型別 (即Type) 物件</param>
046    Private Sub ShowGetNestedTypes(type As Type)
047        Console.WriteLine("取得型別NestedTypes")
048        For Each gnt As Type In type.GetNestedTypes
049            Console.WriteLine("{0}", gnt.Name)
050        Next
051        Console.WriteLine()
052        Console.ReadLine()
053    End Sub
054 
055    ''' <summary>
056    ''' 簡單測試及顯示型別 GetMembers() 方法
057    ''' </summary>
058    ''' <param name="type">型別 (即Type) 物件</param>
059    Private Sub ShowGetMembers(type As Type)
060        Console.WriteLine("取得型別Members")
061        For Each mi As MemberInfo In type.GetMembers()
062            Console.WriteLine("    Member的 Type 及 名稱")
063            Console.WriteLine("          {0}:{1}", mi.MemberType, mi.Name)
064            ShowMemberType(mi)
065        Next
066        Console.WriteLine()
067        Console.ReadLine()
068    End Sub
069 
070    ''' <summary>
071    ''' 簡單測試及顯示MemberInfo裡MemberType成員,目前只讓 ShowGetMembers 呼叫
072    ''' </summary>
073    ''' <param name="mi">MemberInfo物件</param>
074    Private Sub ShowMemberType(mi As MemberInfo)
075        ' MemberInfo 可藉由 MemberTypes 來判斷出型別的不同成員,注意,都是public。
076        ' 簡單測試 MemberTypes.Property
077        Select Case mi.MemberType
078            Case MemberTypes.Constructor
079            Case MemberTypes.Custom
080            Case MemberTypes.Event
081            Case MemberTypes.Field
082            Case MemberTypes.Method
083            Case MemberTypes.NestedType
084            Case MemberTypes.Property
085                Dim pi As PropertyInfo = CType(mi, PropertyInfo)
086                Console.WriteLine("          Property Type: {0}", pi.PropertyType.Name)
087            Case MemberTypes.TypeInfo
088            Case MemberTypes.All
089            Case Else
090                Console.WriteLine("什麼!完全沒有符合的成員!")
091        End Select
092    End Sub
093 
094    ''' <summary>
095    ''' 簡單測試及顯示型別 GetConstructors 方法
096    ''' </summary>
097    ''' <param name="type">型別 (即Type) 物件</param>
098    Sub ShowGetConstructors(type As Type)
099        Console.WriteLine("取得型別Constructors")
100        For Each ci As ConstructorInfo In type.GetConstructors
101            Console.WriteLine("    IsSecuritySafeCritical: {0}", ci.IsSecuritySafeCritical)
102        Next
103        Console.WriteLine()
104        Console.ReadLine()
105    End Sub
106 
107    ''' <summary>
108    ''' 簡單測試及顯示型別 GetEvents 方法
109    ''' </summary>
110    ''' <param name="type">型別 (即Type) 物件</param>
111    Sub ShowGetEvent(type As Type)
112        Console.WriteLine("取得型別Events")
113        For Each ei As EventInfo In type.GetEvents
114            Console.WriteLine("    DeclaringType: {0}", ei.DeclaringType)
115        Next
116        Console.WriteLine()
117        Console.ReadLine()
118    End Sub
119 
120    ''' <summary>
121    ''' 簡單測試及顯示型別 GetFields 方法
122    ''' </summary>
123    ''' <param name="type">型別 (即Type) 物件</param>
124    Sub ShowGetFields(type As Type)
125        Console.WriteLine("取得型別Fields")
126        For Each fi As FieldInfo In type.GetFields
127            Console.WriteLine("    FieldType: {0}", fi.FieldType)
128        Next
129        Console.WriteLine()
130        Console.ReadLine()
131    End Sub
132 
133    ''' <summary>
134    ''' 簡單測試及顯示型別 GetMethods 方法
135    ''' </summary>
136    ''' <param name="type">型別 (即Type) 物件</param>
137    Sub ShowGetMethods(type As Type)
138        Console.WriteLine("取得型別Methods")
139        For Each mi As MethodInfo In type.GetMethods
140            Console.WriteLine("Method: {0}, IsPublic: {1}", mi.GetBaseDefinition.Name, mi.IsPublic)
141        Next
142        Console.WriteLine()
143        Console.ReadLine()
144    End Sub
145 
146#End Region


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

1Dim t() As Object = {"String", 1, 0.5, True}
2ShowTypeFromObject(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(型別),是比較簡單的。

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


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

我們來看執行結果:

VB GetType取得:
Type: System.Int32

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

結論


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

參考資料


沒有留言:

張貼留言

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