標籤:winform style blog http io color ar os 使用
1. 反射的定義
定義:審查中繼資料並收集關於它的類型資訊的能力。中繼資料(編譯以後的最基本資料單元)就是一大堆的表,當編譯器集或者模組時,編譯器會建立一個類定義表,一個欄位定義表,和一個方法定義表等,。
程式集包含模組,而模組包含類型,類型又包含成員。反射則提供了封裝程式集、模組和類型的對象。使用反射動態地建立類型的執行個體,將類型綁定到現有對象,或從現有對象中擷取類型。然後,可以調用類型的方法或訪問其欄位和屬性。
2. 反射的特點
2.1 反射通常具有以下用途。
(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瞭解參數的名稱、資料類型、是輸入參數還是輸出參數,以及參數在方法簽名中的位置等。
2.2 反射的作用:
1. 可以使用反射動態地建立類型的執行個體,將類型綁定到現有對象,或從現 有對象中擷取類型
2. 應用程式需要在運行時從某個特定的程式集中載入一個特定的類型,以便實現某個任務時可以用到反射。
3. 反射主要應用與類庫,這些類庫需要知道一個類型的定義,以便提供更多的功能。
應用要點:
1. 現實應用程式中很少有應用程式需要使用反射類型
2. 使用反射動態綁定需要犧牲效能
3. 有些中繼資料資訊是不能通過反射擷取的
4. 某些反射類型是專門為那些clr 開發編譯器的開發使用的,所以你要意識到不是所有的反射類型都是適合每個人的。
2.3 反射類所使用的設計模式(註:資訊從MSDN擷取)
System.Reflection 命名空間中最常用的方法都使用統一的模式。Module、Type 和 MemberInfo 類的成員使用下表中所示的設計模式。
成員簽名 |
說明 |
MyInstance[] FindXxx(filter,filterCriteria) |
尋找並返回經過篩選的類型列表,或者在當前類型沒有實現任何匹配篩選器的類型的情況下返回空數組。 樣本:System.Type.FindInterfaces(System.Reflection.TypeFilter,System.Object) |
MyInstance GetXxx(<parameters>) |
返回由 <parameters> 唯一指定的類型。如果不存在這樣的類型,成員將返回Null 參考(在 Visual Basic 中為 Nothing)。請注意,<parameters> 唯一地指定一個執行個體。 樣本:System.Type.GetInterface(System.String) |
MyInstance[] GetXxxs() |
返回所有公用類型。如果不存在公用類型,成員將返回空數組。 樣本:System.Type.GetFields |
MyInstance[] GetXxxs(<parameters>) |
返回由 <parameters> 指定的所有類型。如果不存在這樣的類型,成員將返回空數組。請注意,<parameters> 並不一定指定唯一的執行個體。 |
另一個常用的設計模式是使用委託。它們通常在反射中用來支援對返回對象數組的方法的結果集進行篩選。
3. 實現反射
using System.Reflection;//擷取程式集的Assembly 對象和模組時所必需的文法Assembly a = typeof(Object).Module.Assembly; 3.1 反射AppDomain中包含的所有程式集 //通過GetAssemblies 調用appDomain的所有程式集 foreach (Assembly assem in Appdomain.currentDomain.GetAssemblies()) { //反射當前程式集的資訊 reflector.ReflectOnAssembly(assem); }3.2查看類型資訊//從已載入的程式集中擷取Type 對象 Assembly a = Assembly.LoadFrom("05WinFormTest.exe"); Type[] types = a.GetTypes(); foreach (Type t in types) { Console.WriteLine(t.FullName); }//下面的樣本顯示如何列出一個類(此樣本中為 System.String 類)的建構函式using System;using System.Reflection;class ListMembers{ public static void Main(String[] args) { Type t = typeof(System.String); Console.WriteLine("Listing all the public constructors of the {0} type", t); // Constructors. ConstructorInfo[] ci = t.GetConstructors(BindingFlags.Public | BindingFlags.Instance); Console.WriteLine("//Constructors"); PrintMembers(ci); } public static void PrintMembers(MemberInfo[] ms) { foreach (MemberInfo m in ms) { Console.WriteLine("{0}{1}", " ", m); } Console.WriteLine(); }}//其他方法也是一樣 //擷取類型的欄位資訊 FieldInfo [] myfields=type.GetFiedls(); //擷取方法資訊 MethodInfo myMethodInfo=type.GetMethods(); //擷取屬性資訊 PropertyInfo [] myproperties=type.GetProperties(); //擷取事件資訊 EventInfo[] Myevents = type.GetEvents();
具體的可察看MSDN http://msdn2.microsoft.com/zh-cn/library/t0cs7xez(VS.80).aspx
3.3 動態載入和使用類型
篇幅有限,這裡不多講,具體參照http://msdn2.microsoft.com/zh-cn/library/k3a58006(VS.80).aspx
3.4 反射的效能:
使用反射來調用類型或者觸發方法,或者訪問一個欄位或者屬性時clr 需 要做更多的工作:校正參數,檢查許可權等等,所以速度是非常慢的。所以盡量不要使用反射進行編程,對於打算編寫一個動態構造類型(晚綁定)的應用程式,可以採取以下的幾種方式進行代替:
1. 通過類的繼承關係。讓該類型從一個編譯時間可知的基礎類型派生出來,在運行時產生該類 型的一個執行個體,將對其的引用放到其基礎類型的一個變數中,然後調用該基礎類型的虛方法。
2. 通過介面實現。在運行時,構建該類型的一個執行個體,將對其的引用放到其介面類型的一個變數中,然後調用該介面定義的虛方法。
3.通過委託實現。讓該類型實現一個方法,其名稱和原型都與一個在編譯時間就已知的委託相符。在運行時先構造該類型的執行個體,然後在用該方法的對象及名稱構造出該委託的執行個體,接著通過委託調用你想要的方法。這個方法相對與前面兩個方法所作的工作要多一些,效率更低一些
4. PetShop 4.0中所用到的反射
以DALFactory項目為例。通過引用IDAL,讀取Web.Config裡設定的程式集,動態建立類的執行個體,再返回給DALFactory.(關於原廠模式,我也無法理解得很透徹,個人認為原廠模式最大的好處還是在於 方便維護.)
在DALFactory的DataAccess類是一個sealed類,其中建立各種資料對象的方法,均為靜態方法。對Web.Config和反射的運用使其達到抽象工廠的目的。如下所示
public sealed class DataAccess { // Look up the DAL implementation we should be using private static readonly string path = ConfigurationManager.AppSettings["WebDAL"]; private static readonly string orderPath = ConfigurationManager.AppSettings["OrdersDAL"]; private DataAccess() { } public static PetShop.IDAL.ICategory CreateCategory() { string className = path + ".Category"; return (PetShop.IDAL.ICategory)Assembly.Load(path).CreateInstance(className); }
由於命名空間統一存放在Web.Config裡,類也是動態建立,可以避免因為命名空間改變導致的一系列麻煩修改。
不過,反射這一特性會大量損耗效能,所以除非是維護需要,不然可通過其他方式解決。
C# 反射