反射到底有什么用?
以前,假設我們要不借助任何類庫直接解析Json協議的東西,比如{“a”:"A","b":"B","c":"C"},并且我們需要將他們賦給一個與之對應的實體類。那么我們需要寫出如下的代碼來解析
//這只是一段偽代碼 while(str.Length!=0){//在一個大的while里面遍歷整個字符串 //這里省去解析的函數 //用判斷讀到的名稱,然后對應相應的屬性進行初始化。 if(name=="a"){ SetA(value);//為屬性A賦值 }else if(name=="b"){//為屬性B賦值 SetA(value); }else{ ... } }
從上面代碼中,可以知道,當要為一個實體類賦值和解析的屬性比較少的時候,我們要寫的代碼還是可以讓人接受的,但是一旦實體類的代碼一多,那么整個過程就非常地惡心。通過反射,我們可以獲取一個實體類中的信息,通過獲取運行時的函數信息,我們又可以動態地調用函數,為實體類動態賦值。以上代碼如果用反射的相關方法來寫的話,就會變得異常簡潔。
//這只是一段偽代碼 while(str.Length!=0){//在一個大的while里面遍歷整個字符串 //這里省去解析的函數 //用判斷讀到的名稱,然后通過反射調用相應的屬性進行初始化。 MethodInfo mMethodInfo = ClassA.GetMethod(name); mMethodInfo.Invoke(obj1, null);
…
}
說了這么久,給反射下個定義
反射的定義:審查元數據并收集關于它的類型信息的能力。元數據(編譯以后的最基本數據單元)就是一大堆的表,當編譯程序集或者模塊時,編譯器會創建一個類定義表,一個字段定義表,和一個方法定義表等,。System.reflection命名空間包含的幾個類,允許你反射(解析)這些元數據表的代碼和反射相關的命名空間(我們就是通過這幾個命名空間訪問反射信息): – 引用自《C# 反射技術應用》
System.Reflection.MemberInfo
System.Reflection.EventInfo
System.Reflection.FieldInfo
System.Reflection.MethodBase
System.Reflection.ConstructorInfo
System.Reflection.MethodInfo
System.Reflection.PropertyInfo
System.Type
System.Reflection.Assembly
來一段代碼示例
我們有一個HelloWorld類,我們想要通過反射動態地獲取他的信息,并動態地調用他的方法。
public class HelloWorld { string myName = null; public HelloWorld(string name) { myName = name; } public HelloWorld() : this(null) { } public string Name { get { return myName; } } public void SayHello() { if (myName == null) System.Console.WriteLine("\t\tHello World"); else System.Console.WriteLine("\t\tHello," + myName); } }
下面我們將應用反射進行獲取方法名、動態創建類的實例、動態調用方法
static void Main(string[] args) { System.Console.WriteLine("列出程序集中的所有類型"); Assembly mAssembly = Assembly.LoadFrom("ReflectionExample.exe"); Console.WriteLine("程序集的名稱是: {0}\n\n", mAssembly.GetName()); Type[] mType = mAssembly.GetTypes(); Console.WriteLine("以下是程序集里面的類型"); for (int i = 0; i < mType.Length; i++) { Console.WriteLine("\t{0}.{1}", i, mType[i].Name); } Type Ht = typeof(HelloWorld); MethodInfo[] mMethodInfo = Ht.GetMethods();//利用反射獲取HelloWorld里所有的方法 Console.WriteLine("Hello World類中所有的方法:"); for (int i = 0; i < mMethodInfo.Length; i++) { //輸出類里面所有的方法名 Console.WriteLine("\t{0}.{1}", i, mMethodInfo[i].Name); } Console.WriteLine("利用Activator.CreateInstance創建實例"); object obj1 = Activator.CreateInstance(Ht);//利用Activator.CreateInstance創建不同的實例obj1和obj2 string name = "ChenZheRong"; object obj2 = Activator.CreateInstance(Ht, name); MethodInfo mSayHello = Ht.GetMethod("SayHello"); Console.WriteLine("\tobj1的輸出"); mSayHello.Invoke(obj1, null); Console.WriteLine("\tobj2的輸出"); mSayHello.Invoke(obj2, null); }
結果截圖如下所示
要徹底地了解反射,我們還需要了解.NET可執行應用程序的結構。
.NET可執行應用程序結構
程序代碼在編譯后生成可執行的應用,我們首先要了解這種可執行應用程序的結構。
應用程序結構分為應用程序域—程序集—模塊—類型—成員幾個層次,公共語言運行庫加載器管理應用程序域,這種管理包括將每個程序集加載到相應的應用程序域以及控制每個程序集中類型層次結構的內存布局。
程序集包含模塊,而模塊包含類型,類型又包含成員,反射則提供了封裝程序集、模塊和類型的對象。我們可以使用反射動態地創建類型的實例,將類型綁定到現有對象或從現有對象中獲取類型,然后調用類型的方法或訪問其字段和屬性。反射通常具有以下用途。
(1)使用Assembly定義和加載程序集,加載在程序集清單中列出模塊,以及從此程序集中查找類型并創建該類型的實例。
(2)使用Module了解包含模塊的程序集以及模塊中的類等,還可以獲取在模塊上定義的所有全局方法或其他特定的非全局方法。
(3)使用ConstructorInfo了解構造函數的名稱、參數、訪問修飾符(如pulic 或private)和實現詳細信息(如abstract或virtual)等。使用Type的GetConstructors或GetConstructor方法來調用特定的構造函數。
(4)使用MethodInfo了解方法的名稱、返回類型、參數、訪問修飾符(如pulic 或private)和實現詳細信息(如abstract或virtual)等。使用Type的GetMethods或GetMethod方法來調用特定的方法。
(5)使用FiedInfo了解字段的名稱、訪問修飾符(如public或private)和實現詳細信息(如static)等,并獲取或設置字段值。
(6)使用EventInfo了解事件的名稱、事件處理程序數據類型、自定義屬性、聲明類型和反射類型等,添加或移除事件處理程序。
(7)使用PropertyInfo了解屬性的名稱、數據類型、聲明類型、反射類型和只讀或可寫狀態等,獲取或設置屬性值。
(8)使用ParameterInfo了解參數的名稱、數據類型、是輸入參數還是輸出參數,以及參數在方法簽名中的位置等。
System.Reflection.Emit命名空間的類提供了一種特殊形式的反射,可以在運行時構造類型。
參考連接
文章列表