前面章節,已經說明了組件與模組的用法,接下來要說明的是型別的反射。
取得型别 (Type)
要取得一個型別物件 ( Type ),可透過以下幾個方式:
- 由組件 ( Assembly ) 類別中的 Assembly.GetType 方法取得。
- 由模組 ( Module ) 類別中的 Module.GetType 方法取得。
- 由物件 ( Object ) 執行個體的 Object.GetType 方法取得。
- C# 的 typeof 運算子與 Visual Basic 的 GetType 運算子,都可取得型別的 Type 物件。
底下示範各種取得型別物件的方法,並透過型別的執行個體取得型別的屬性:
string dll_file = @"D:\MCTS\MCTS2011\Practice\ClassLibrary1\bin\Debug\ClassLibrary1.dll"; Assembly asm = Assembly.LoadFrom(dll_file); //===================================== // 由 Assembly 取得型別 //===================================== Type empType = asm.GetType("ClassLibrary1.Employee"); //取得某一個型別 Type[] types1 = asm.GetTypes(); //取得組件中所有型別 //ClassLibrary1.TestClass //ClassLibrary1.Employee //ClassLibrary1.Class2 //ClassLibrary1.Class1 //ClassLibrary1.ClassB //ClassLibrary1.ClassA //===================================== // 由 Module 取得型別 //===================================== Module[] mods = asm.GetModules(); Module m = mods[0]; Type[] types2 = m.GetTypes(); //===================================== // 由 Object執行個體 取得型別 //===================================== object o = new object(); Type type3 = o.GetType(); //===================================== // 由 typeof 取得型別 //===================================== Type types4 = typeof(Int32); //System.Int32 Type type5 = typeof(ClassLibrary1.ClassA); //ClassLibrary1.ClassA // 透過型別的執行個體取得型別的屬性 Type t = typeof(String); Console.WriteLine("Type: {0}", t.Name); //String Console.WriteLine(" Namespace : {0}", t.Namespace); //System Console.WriteLine(" FullName : {0}", t.FullName); //System.String Console.WriteLine(" Is ValueType?: {0}", t.IsValueType); //False Console.WriteLine(" Is Sealed? : {0}", t.IsSealed); //True Console.WriteLine(" Is Abstract? : {0}", t.IsAbstract); //False Console.WriteLine(" Is Public? : {0}", t.IsPublic); //True Console.WriteLine(" Is Class? : {0}", t.IsClass); //True Console.WriteLine(" Is IsEnum? : {0}", t.IsEnum); //False
列舉型别的成員
MemberInfo
型别的成員包含欄位、方法、屬性、事件、介面等等的資訊。 在取得某型別物件後,可以透過 Type 類別所提供的方法和屬性,進一步取得這些相關的成員資訊。
透過 GetMembers 方法,可以取得所有的成員資訊。 若只想取得特定類型的成員資訊則可以叫用相對應的方法,例如:
- GetField :取得目前 Type 的特定欄位。
- GetFields :傳回目前 Type 的所有公用欄位。 FieldInfo 。
- GetEvent :取得由目前 Type 所宣告或繼承的特定事件。
- GetEvents :取得由目前 Type 所宣告或繼承的所有公用事件。 EventInfo 。
- GetMethod :取得目前 Type 的特定方法。
- GetMethods :傳回目前 Type 的所有公用成員。 MethodInfo 。
- GetProperty :取得目前 Type 的特定屬性。
- GetProperties :傳回目前 Type 的所有公用屬性。 PropertyInfo 。
- GetConstructor :取得目前 Type 的特定建構函式。
- GetConstructors :取得目前 Type 的所有建構函式。 ConstructorInfo 。
- GetInterface :取得由目前 Type 所實作或繼承的特定介面。
- GetInterfaces :可以取得目前 Type 所實作的所有介面。
透過這些方法回傳的型别成員都是由抽象類別 MemberInfo 中繼承而來。 且其類別名稱都帶有 Info 結尾,例如:
- FieldInfo :表示欄位資訊的類別
- EventInfo :表示事件資訊的類別
- MethodInfo :表示方法資訊的類別
- PropertyInfo :表示屬性資訊的類別
- ConstructorInfo :表示類別建構函式資訊的類別
- LocalVariableInfo :表示區域變數的類別
- MethodBase :提供有關方法和建構函式的資訊。
- Type :代表型別宣告:類別型別、介面型別、陣列型別、值型別、列舉型別、型別參數、泛型型別定義,以及開放式或封閉式的建構泛型型別。
// 由 Assembly 取得型別 string asm_file = @"D:\MCTS\MCTS2011\Practice\ClassLibrary1\bin\Debug\ClassLibrary1.dll"; Assembly asm = Assembly.LoadFrom(asm_file); Type t = asm.GetType("ClassLibrary1.Employee"); //顯示 Constructor 資訊 foreach (ConstructorInfo constructor in t.GetConstructors()) { Console.WriteLine("[{0}] {1}", constructor.MemberType, constructor.Name); } //顯示 Method 資訊 BindingFlags flags = BindingFlags.DeclaredOnly; foreach (MethodInfo method in t.GetMethods()) { Console.WriteLine("[{0}] {1}", method.MemberType, method.Name); } //顯示 Field 資訊 foreach (FieldInfo field in t.GetFields()) { Console.WriteLine("[{0}] {1}", field.MemberType, field.Name); } //顯示 Property 資訊 foreach (PropertyInfo prop in t.GetProperties()) { Console.WriteLine("[{0}] {1}", prop.MemberType, prop.Name); } //顯示 Event 資訊 foreach (EventInfo even in t.GetEvents()) { Console.WriteLine("[{0}] {1}", even.MemberType, even.Name); } //顯示 Interface 資訊 foreach ( Type type in t.GetInterfaces()) { Console.WriteLine("[{0}] {1}", type.MemberType, type.Name); } //顯示 所有成員 資訊 foreach (MemberInfo member in t.GetMembers()) { Console.WriteLine("[{0}] {1}", member.MemberType, member.Name); } //[Method] get_EmpNum //[Method] set_EmpNum //[Method] set_EmpName //[Method] get_EmpName //[Method] get_Age //[Method] set_Age //[Method] ToString //[Method] Equals //[Method] GetHashCode //[Method] GetType //[Constructor] .ctor //[Constructor] .ctor //[Property] EmpNum //[Property] EmpName //[Property] Age
MethodBody
以上的說明方法,僅僅取得型別的結構,並沒有取得程式碼部分,這部分就必須透過 MethodBody 類別來取得。 MethodBody 是一個特殊的物件,裝載著區域變數和IL指令碼。 要取得 MethodBody 可以透過 MethodBase ( ConstructorInfo , MethodInfo 等類別 ) 的執行個體,透過呼叫 GetMethodBody 來取得。
MethodBody 類別中,幾個重要的屬性與方法:
- MaxStackSize :取得執行此方法時,運算元堆疊上的最大項目數。
- LocalVariables :取得方法主體中所宣告之區域變數的清單。
- GetILAsByteArray :傳回方法主體的 MSIL,當做位元組陣列。
string dll_file = @"D:\MCTS\MCTS2011\Practice\ClassLibrary1\bin\Debug\ClassLibrary1.dll"; Assembly asm = Assembly.LoadFile(dll_file); Type classA = asm.GetType("ClassLibrary1.Employee"); //1)先取得想要追縱的方法 MethodInfo method = classA.GetMethod("ShowData"); //2)再由方法的執行個體,透過 GetMethodBody 取得 MethodBody MethodBody body = method.GetMethodBody(); //取得 堆疊大小 Console.WriteLine(" MaxStack: {0}", body.MaxStackSize); //列舉 變數 foreach (LocalVariableInfo local in body.LocalVariables) { Console.WriteLine("[{0}] Var({1})", local.LocalType, local.LocalIndex); } //[System.String] Var(0) //[System.String] Var(1) //[System.String] Var(2) //[System.Int32] Var(3) //[System.String[]] Var(4) //列舉 IL foreach (Byte b in body.GetILAsByteArray()) { Console.Write("{0:x2} ", b); } //00 02 28 16 00 00 06 0d 12 03 ...... 00 0a 0b 07 0c 2b 00 08 2a
BindingFlags
BindingFlags 是一個列舉型別, 用來控制反射的繫結旗標,這個旗標是用來指定 GetMembers 方法的搜尋方式。
- IgnoreCase:不考慮成員名稱的大小寫。
- DeclaredOnly:只考慮型別本身的成員。不考慮繼承成員。
- Instance:執行個體成員
- Static:靜態成員
- Public:公用成員
- NonPublic:非公用成員
- FlattenHierarchy:
例如前面一個例子,因為沒有指明 BindingFlags ,所以 GetMembers 僅會回傳公用的成員,下面這個例子,則會回傳私用的成員:
//取得組件型別 Assembly asm = Assembly.LoadFrom(@"D:\MCTS\MCTS2011\Practice\ClassLibrary1\bin\Debug\ClassLibrary1.dll"); Type empType = asm.GetType("ClassLibrary1.Employee"); //依指定的 BindingFlags , 列舉所有成員 BindingFlags flags = BindingFlags.NonPublic | BindingFlags.Instance; foreach (MemberInfo member in empType.GetMembers(flags)) { Console.WriteLine("[{0}] {1}", member.MemberType, member.Name); } //[Method] MyNumB //[Method] Finalize //[Method] MemberwiseClone //[Field] EmpNumB //[Field] <EmpName≷k__BackingField //[Field] <Age≷k__BackingField
備註:
指定 BindingFlags 時,必須指定 Instance 或 Static 與 Public 或 NonPublic,否則不會傳回成員。
沒有留言:
張貼留言