一、元數據和反射
1.1 定義
大多數程序都要處理數據,包括讀、寫、操作和顯示數據。然而,對於某些程序來說,它們操作的不是數字、文本或圖形,而是程序和程序類型本身的信息。
● 有關程序及其類型的數據被稱為元數據(metadata),它們保存在程序的程序集中
● 程序在運行時,可以查看其他程序集或其本身的元數據。一個運行的程序查看本身的元數據和其他程序的元數據的行為叫做反射(reflection)
反射提供了封裝程序集、模塊和類型的對象(Type類型)。可以使用反射動態創建類型的實例,將類型綁定到現有對象,或從現有對象獲取類型並調用其方法或訪問其字段和屬性。如果代碼中使用了特性,可以利用反射對它們進行訪問。【MSDN】
1.2 使用場景
● 需要訪問程序元數據的特性
● 檢查和實例化程序集中的類型
● 在運行時構建新類型。使用System.Reflection.Emit中的類
● 執行后期綁定,訪問在運行時創建的類型的方法。【MSDN】
1.3 形象說明
地球的內部結構:地球的內部結構大體可以分為三層:地殼、地幔和地核。地殼是固體,地核是液體,地幔則是半液半固的結構(地理知識)。如何在地球表面不用深入地球內部就知道其內部結構呢?答案是:向地球發射“地震
波”。地震波分兩種,一種是橫波,另一種是縱波。橫波只能穿透固體,而縱波既可以穿透固體又可以穿透液體。通過在地面對縱波和橫波的返回情況,我們就可以大體斷定地球內部的構造了。
B型超聲波:大家體檢的時候都做過B超吧,B超可以透過肚皮探測到你的內臟的生理情況。這是如何做到的呢?答案是:它可以透過肚皮向你體內發射B型超聲波,當超聲波遇到內臟壁的時候就會產生一定的“回音”反射,然后
把“回音”進行處理就可以顯示出你的內臟的情況了。(部分細節不予追究)
大家注意到這兩個例子的共同特點,就是從一個對象的外部去了解對象內部的構造,而且都是利用了波的反射功能。在.NET中的反射也可以實現從對象的外部來了解對象(或程序集)內部結構的功能,哪怕你不知道這個對象(或
程序集)是什么,另外.NET中的反射還可以動態創建出對象並執行它其中的方法。
反射是.NET中重要的機制,通過反射,可以在運行時獲得程序或程序集中每一個類型(包括類、結構、委托、接口和枚舉等)的成員和成員的信息。有了反射,即可對每一個類型了如指掌。另外我還可以直接創建對象,即使這個對象的類型在編譯時還不知道。
1.4 反射的主要用途
-
使用 Assembly 定義和加載程序集,加載在程序集清單中列出的模塊,以及從此程序集中查找類型並創建該類型的實例。
-
使用 Module 發現以下信息:包含模塊的程序集以及模塊中的類等。 您還可以獲取在模塊上定義的所有全局方法或其他特定的非全局方法。
-
使用 ConstructorInfo 發現以下信息:構造函數的名稱、參數、訪問修飾符(如 public 或 private)和實現詳細信息(如 abstract 或 virtual)等。 使用 Type 的 GetConstructors 或 GetConstructor 方法來調用特定的構造函數。
-
使用 MethodInfo 發現以下信息:方法的名稱、返回類型、參數、訪問修飾符(如 public 或 private)和實現詳細信息(如 abstract 或 virtual)等。 使用 Type 的 GetMethods 或 GetMethod 方法來調用特定的方法。
-
使用 FieldInfo 發現以下信息:字段的名稱、訪問修飾符(如 public 或 private)和實現詳細信息(如 static)等;並獲取或設置字段值。
-
使用 EventInfo 發現以下信息:事件的名稱、事件處理程序數據類型、自定義特性、聲明類型和反射類型等;並添加或移除事件處理程序。
-
使用 PropertyInfo 發現以下信息:屬性的名稱、數據類型、聲明類型、反射類型和只讀或可寫狀態等;並獲取或設置屬性值。
-
使用 ParameterInfo 發現以下信息:參數的名稱、數據類型、參數是輸入參數還是輸出參數,以及參數在方法簽名中的位置等。
-
當您在一個應用程序域的僅反射上下文中工作時,請使用 CustomAttributeData 來發現有關自定義特性的信息。 通過使用 CustomAttributeData,您不必創建特性的實例就可以檢查它們。【MSDN】
注:要使用反射,必須使用System.Reflection命名空間
二、Type類
2.1 簡介
BCL聲明了一個叫做Type的抽象類,它被設計用來包含類型的特性,使用這個類的對象能讓我們獲取程序使用的類型的信息。
由於Type是抽象類,因此它不能有實例,而是在運行時CLR創建從Type(RuntimeType)派生的類的實例。Type包含了類型信息,當我們要訪問這些實例時,CLR不會返回派生類的引用而是返回Type基類的引用。為了簡單起見,下面篇幅中我會把引用所指向的對象稱為Type類型的對象。
Type類的重要事項:
● 對於程序中用到的每一個類型,CLR都會創建一個包含這個類型信息的Type類型的對象
● 程序中用到的每一個類型都會關聯到獨立的Type類型的對象
● 不管創建的類型有多少個實例,只有一個Type對象會關聯到所有這些實例
如果你對C#中有哪些類型不是很了解,那么知識擴展: C#中的類型有:預定義的類型(int,long和string等),BCL中的類型(Console,IEnumerable等)以及用戶自定義的類型(MyClass,MyDel等)
2.2 Type類的部分常見成員
成員 | 成員類型 | 描述 |
Name | 屬性 | 返回類型的名字 |
FullName | 屬性 | 返回數據類型的完全限定名(包括命名空間名) |
NameSpace | 屬性 | 返回包含數據類型聲明的命名空間 |
Assembly | 屬性 | 返回聲明類型的程序集。如果類型是泛型的,返回定義這個類型的程序集 |
GetConstructor(), GetConstructors() | 方法 | 返回ConstructorInfo類型,用於取得該類的構造函數的信息 |
GetEvent(), GetEvents() | 方法 | 返回EventInfo類型,用於取得該類的事件的信息 |
GetField(), GetFields() | 方法 | 返回FieldInfo類型,用於取得該類的字段(成員變量)的信息 |
GetInterface(), GetInterfaces() | 方法 | 返回InterfaceInfo類型,用於取得該類實現的接口的信息 |
GetMember(), GetMembers() | 方法 | 返回MemberInfo類型,用於取得該類的所有成員的信息 |
GetMethod(), GetMethods() | 方法 | 返回MethodInfo類型,用於取得該類的方法的信息 |
GetProperty(), GetProperties() | 方法 | 返回PropertyInfo類型,用於取得該類的屬性的信息 |
有關Type類的詳細信息請參見【MSDN】
2.3 獲取Type對象
我們可以使用GetType方法和typeof運算符來獲取Type對象。object類型包含了一個叫做GetType的方法,它返回對實例的Type對象的引用。由於每一個類型最終都是從object繼承的,所以我們可以在任何類型對象上使用GetType方法來獲取它的Type對象
a. GetType()用法示例
string str = "fun"; Type t = str.GetType();
還有一種靜態方法:public static Type GetType (string typeName);
Type myType1 = Type.GetType("System.Int32");
備注:如果typeName
找不到,在調用GetType(String)方法將返回null
。 它不會引發異常。
b. typeof用法
Type t1 = typeof(string);
2.4 示例
using System; using System.Reflection; namespace Type類使用 { class Base { public int BaseField = 0; } class Derived : Base { public int DerivedField = 0; public int change { set; get; } } class Program { static void Main(string[] args) { Base b = new Base(); Derived d = new Derived(); Base[] bd = new Base[] { b, d }; foreach(var val in bd) { Type t = val.GetType(); //獲取類型 Console.WriteLine("object type:{0}", t.Name); FieldInfo[] fi = t.GetFields(); //獲取字段信息 foreach(var f in fi) { Console.WriteLine(" Field: {0}", f.Name); } } Console.ReadKey(); } } }
上面的例子,是對類中public修飾的成員的操作,實際上private和protected修飾的成員也可以訪問到,詳情請看:C#之玩轉反射
【備注:本文參考了 C#反射詳解 ,如有冒犯請聯系刪除】