2012年3月21日 星期三

反射組件的型別

前面章節,已經說明了組件與模組的用法,接下來要說明的是型別的反射。

取得型别 (Type)

要取得一個型別物件 ( 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 方法,可以取得所有的成員資訊。 若只想取得特定類型的成員資訊則可以叫用相對應的方法,例如:

透過這些方法回傳的型别成員都是由抽象類別 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&gl;k__BackingField
//[Field] <Age&gl;k__BackingField

備註:

指定 BindingFlags 時,必須指定 Instance 或 Static 與 Public 或 NonPublic,否則不會傳回成員。

沒有留言:

張貼留言