C#編譯后的文件主要由IL代碼和元數據組成,元數據為.NET組件提供了豐富的自描述特性,它使得我們可以在代碼運行時獲知組件中的類型等重要的信息。C#中這是通過一種稱作映射(Reflection)的機制來完成的。
動態類型查詢
首先創建一個簡單的類型:
namespace ReflectionClass { public class MyClass { #region Property private int m_Count = 100; public int Count { get { return m_Count; } set { m_Count = value; } } #endregion #region Method public void Print() { Console.WriteLine("MyClass.Count = {}", Count); } #endregion } }
編譯后可以得到“ReflectionClass.dll”文件,接下來實現查詢類型的測試程序:
namespace TestReflection { public class App { static void Main(string[] args) { Type type = typeof(MyClass); //獲取MyClass的類型信息 Console.WriteLine("The Type Name : {0}", type.Name); //獲取類型的名字 FieldInfo[] fieldArray = type.GetFields(); //獲取所有的公有域 Console.Write("The {0} Fields : ", fieldArray.Length); foreach (FieldInfo field in fieldArray) { Console.Write(field.Name + " "); } Console.WriteLine(); PropertyInfo[] propertyArray = type.GetProperties(); //獲取所有的公有屬性 Console.Write("The {0} Properties : ", propertyArray.Length); foreach (PropertyInfo property in propertyArray) { Console.Write(property.Name + " "); } Console.WriteLine(); MethodInfo[] methodArray = type.GetMethods(); //獲取所有的公有方法 Console.Write("The {0} Methods : ", methodArray.Length); foreach (MethodInfo method in methodArray) { Console.Write(method.Name + " "); } } } }
編譯后執行,可以得到以下輸出:
The Type Name : MyClass
The 0 Fields :
The 1 Properties : Count
The 7 Methods : get_Count set_Count Print ToString Equals GetHashCode GetType
在上面的例子中,首先通過 typeof(MyClass)獲得MyClass類的類型信息,當然也可以通過創建對象實例,然后調用對象實例的GetType方法來獲得(每個類都從 object根類中繼承獲得此方法)。在擁有了類型信息(變量type)后,便可以獲得其類型的名字、該類型含有的公有域、公有屬性、公有方法。注意: 這里C#的映射機制只允許獲取類型的公有信息,這符合面向對象的封裝原則。其中4個方法(GetHashCode、Equals、ToString、GetType)都是繼承自object類的公有方法,而方法get_Count 和set_Count則是實現Count屬性的“副產物。實際上,System.Type類各種各樣的成員使得我們能夠獲得幾乎所有與類型相關的公有信息。在System.Reflection命名空間下的各個類都可以獲得各個編程元素較詳細的信息,如方法的參數與返回值、域的類型、枚舉的各個值等。
動態創建與調用
實際上映射遠不止動態地獲知組件的類型信息,它還能在獲得類型信息的基礎上,在代碼運行時進行類型的動態創建與方法的動態調用。
namespace TestReflection2 { public class App { static void Main(string[] args) { Assembly assemlby = Assembly.LoadFrom("ReflectionClass.dll"); //裝載組件 foreach (var type in assemlby.GetTypes()) { if (type.IsClass && !type.IsAbstract) { MethodInfo[] methodArray = type.GetMethods(); //獲得類型的公有方法 object obj = Activator.CreateInstance(type); //創建實例(無參構造器) foreach (var method in methodArray) { if (!method.IsAbstract && !method.IsStatic && method.GetParameters().Length == 0) { object ret = method.Invoke(obj, null); //調用實例方法 Console.WriteLine("{0}'s Return : {1}", method.Name, ret); } } } } } } }
編譯后執行,可以得到以下輸出:
get_Count's Return : 100
MyClass.Count = 100
Print's Return :
ToString's Return : ReflectionClass.MyClass
GetHashCode's Return : 62476613
GetType's Return : ReflectionClass.MyClass
在上面的例子中給出了被動態調用的方法名字和返回值。其中第二行輸出的“MyClass.Count = 100”,它是動態調用方法MyClass.Print()的輸出。需要指出的是調用的是類型的公有無參數實例方法。給出組件的名字,應用Assembly.LoadFrom,我們便可以動態地裝載組件。 Activator.CreateInstance()允許動態地創建類型(這里只通過無參數的構造器來創建),實際上用它創建出來的類型和用“MyClass obj = new MyClass()”創建出來的類型一樣。進而,還可以在查詢到的成員的基礎上,對它們進行動態調用。
另外,還可以用“Assembly.CreateInstance()”創建實例,從本質上講,“Assembly.CreateInstance()”就是調用的“Activator.CreateInstance()”。
