首先瞭解C#反射的概念,反射是一個運行庫類型探索的過程。通過反射可以得到一個給定程式集所包含的所有類型的列表,
這個列表包括給定類型中定義的方法、欄位、屬性和事件。也可以動態發現一組給定類支援的借口、方法的參數和其他
相關資訊如基類、命名空間、資料清單等。
C#反射命名空間詳細介紹:
1.System.Reflection命名空間內的各類型
(1) Assembly通過它可以載入、瞭解和操縱一個程式集
(2) AssemblyName 通過它可以找到大量隱藏在程式集的身份中的資訊,如版本資訊、地區資訊等
(3) EventInfo 事件的資訊
(4) FieldInfo 欄位的資訊
(5) MethodInfo 方法的資訊
(6) ParameterInfo 參數的資訊
(7) PropertyInfo 屬性的資訊
(8) MemberInfo 是抽象基類,為 EventInfo、FieldInfo 、MethodInfo、PropertyInfo等類型定義了公用的行為。
(9) Module 用來訪問帶有多檔案程式集的給定模組
2.System.Type類
System.Type支援的成員可以分為這樣幾類
(1) Is*** 用來檢查一個類型的中繼資料,如IsAbstract、IsClass、IsValueType等等
(2) Get*** 用來從類型得到指定項目,如GetEvent()得到類型的一個指定的事件(EventInfo)。
另外,這些方法都有一個單數版本和一個複數版本。如GetEvent()對應有一個複數版本GetEvents(),
該方法返回一個相關的EventInfo數組。
(3) FindMembers() 根據查詢條件返回一個MemberInfo類型的數組
(4)GetType() 該靜態方法根據一個字串名稱返回一個Type執行個體
(5)InvokeMember() 對給定項目進行晚期綁定
3.得到一個Type類型執行個體的三種方法
因為Type是一個抽象類別,所以不能直接使用new關鍵字建立一個Type對象
(1)使用System.Object.GetType()
Person pe=new Person(); //---------定義pe為person類的一個對象
Type t=pe.GetType();
(2)使用System.Type.GetType()靜態方法,參數為類型的完全限定名
Type t=Type.GetType("Entity.Person"); 該方法被重載,允許指定兩個布爾類型的參數,一個用來控制當前類型不能找到時是否拋出異常,
另一個用來指示是否區分字串大小寫
Type t=Type.GetType("Entity.Person",false,true); 注意到傳入的字串並沒有包含類型所在的程式集資訊,此時該類型便被認為是定義在當前執行的程式集中的。
要得到一個外部私用組件的類型中繼資料時,字串參數必須使用類型完全限定名加上類型所在程式集的易記名稱字
Type t=Type.GetType("Entity.Person","Entity"); //------"Entity"即為類型所在程式集的易記名稱字 巢狀型別:傳入的字串可以指定一個+標記來表示一個巢狀型別,
如希望得到一個嵌套在person類中的枚舉類型City的類型資訊,則可以這樣
Type t=Type.GetType("Entity.person+City");
(3)使用typeof運算子
Type t=typeof(person); 三種方法的比較:
使用第一種方法必須先建立一個執行個體,而後兩種方法不必先建立執行個體。但使用typeof運算子仍然需要知道類型的編譯時間資訊,
而使用System.Type.GetType()靜態方法不需要知道類型的編譯時間資訊,所以是首選方法。
一個最簡單的C#反射執行個體,首先編寫類庫如下:
namespace ReflectionTest
{
public class WriteTest
{
//帶參數的公用方法
public void WriteString(string s, int i)
{
Console.WriteLine("WriteString:" + s + i.ToString());
}
//帶一個參數的靜態方法
public static void StaticWriteString(string s)
{
Console.WriteLine("StaticWriteString:" + s);
}
//不帶參數的靜態方法
public static void NoneParaWriteString()
{
Console.WriteLine("NoParaWriteString");
}
}
}
class TestApp
{
public static void Main()
{
Assembly ass;
Type type;
Object obj;
//用來測試靜態方法
Object any = new Object();
//指定類庫檔案必須使用絕對路徑,不能使用相對路徑
ass = Assembly.LoadFile(@"D:\Source Code\00.C#Sudy\01.Reflection\01\ReflectTest.dll");
//命名空間和類的名字必須一起指定
type = ass.GetType("ReflectionTest.WriteTest");
/**//*example1---------*/
MethodInfo method = type.GetMethod("WriteString");
string test = "test";
int i = 1;
Object[] parametors = new Object[]{test,i};
//在例子1種必須執行個體化反射要反射的類,因為要使用的方法並不是靜態方法。
//建立對象執行個體
obj = ass.CreateInstance("ReflectionTest.WriteTest");
//執行帶參數的公用方法
method.Invoke(obj, parametors);
//method.Invoke(any, parametors);//異常:必須執行個體化反射要反射的類,因為要使用的方法並不是靜態方法。
/**//*example2----------*/
method = type.GetMethod("StaticWriteString");
method.Invoke(null, new string[] { "test"}); //第一個參數忽略
//對於第一個參數是無視的,也就是我們寫什麼都不會被調用,
//即使我們隨便new了一個any這樣的Object,當然這種寫法是不推薦的。
//但是對應在例子1種我們如果Invoke的時候用了類型不一致的執行個體來做為參數的話,將會導致一個運行時的錯誤。
method.Invoke(obj, new string[] { "test"});
method.Invoke(any, new string[] { "test"});
/**//*example3-----------*/
method = type.GetMethod("NoneParaWriteString"); //調用無參數靜態方法的例子,這時候兩個參數我們都不需要指定,用null就可以了。s
method.Invoke(null, null);
}
}
從上面的總結中可以看出,對於外部調用的動態庫應用反射時要用到Assembly.LoadFile(),然後才是擷取類型、執行方法等;
當用反射建立當前程式集中對象執行個體或執行某個類下靜態方法時只需通過Type.GetType("類的完整名")。