C# 反射總結

C# 反射總結

[轉]C#反射
 
反射(Reflection)是.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命名空間的類提供了一種特殊形式的反射,能夠在運行時構造類型。  
反射也可用於建立稱爲類型瀏覽器的應用程序,使用戶可以選擇類型,而後查看有關選定類型的信息。  
此外,Jscript等語言編譯器使用反射來構造符號表。System.Runtime.Serialization命名空間中的類使用反射來訪問數據並肯定要永久保存的字段,System.Runtime.Remoting命名空間中的類經過序列化來間接地使用反射。
2008年03月01日 星期六 下午 07:36
[來源]http://blog.csdn.net/sscsgss/archive/2006/10/19/1341035.aspx 
提綱: 
一、 什麼是反射 
二、 命名空間與裝配件的關係 
三、 運行期獲得類型信息有什麼用 
四、 如何使用反射獲取類型 
五、 如何根據類型來動態建立對象 
六、 如何獲取方法以及動態調用方法 
七、 動態建立委託 

一、什麼是反射 
        Reflection,中文翻譯爲反射。 
        這是.Net中獲取運行時類型信息的方式,.Net的應用程序由幾個部分:‘程序集(Assembly)’、‘模塊(Module)’、‘類型 (class)’組成,而反射提供一種編程的方式,讓程序員能夠在程序運行期得到這幾個組成部分的相關信息,例如: 
        Assembly類能夠得到正在運行的裝配件信息,也能夠動態的加載裝配件,以及在裝配件中查找類型信息,並建立該類型的實例。 
Type類能夠得到對象的類型信息,此信息包含對象的全部要素:方法、構造器、屬性等等,經過Type類能夠獲得這些要素的信息,而且調用之。 
MethodInfo包含方法的信息,經過這個類能夠獲得方法的名稱、參數、返回值等,而且能夠調用之。 
諸如此類,還有FieldInfo、EventInfo等等,這些類都包含在System.Reflection命名空間下。 

二、命名空間與裝配件的關係 
        不少人對這個概念可能仍是很不清晰,對於合格的.Net程序員,有必要對這點進行澄清。 
        命名空間相似與Java的包,但又不徹底等同,由於Java的包必須按照目錄結構來放置,命名空間則不須要。 
        裝配件是.Net應用程序執行的最小單位,編譯出來的.dll、.exe都是裝配件。 
        裝配件和命名空間的關係不是一一對應,也不互相包含,一個裝配件裏面能夠有多個命名空間,一個命名空間也能夠在多個裝配件中存在,這樣說可能有點模糊,舉個例子: 
裝配件A: 
namespace   N1 

      public   class   AC1   {…} 
      public   class   AC2   {…} 

namespace   N2 

      public   class   AC3   {…} 
      public   class   AC4{…} 

裝配件B: 
namespace   N1 

      public   class   BC1   {…} 
      public   class   BC2   {…} 

namespace   N2 

      public   class   BC3   {…} 
      public   class   BC4{…} 

        這兩個裝配件中都有N1和N2兩個命名空間,並且各聲明瞭兩個類,這樣是徹底能夠的,而後咱們在一個應用程序中引用裝配件A,那麼在這個應用程序中,咱們能看到N1下面的類爲AC1和AC2,N2下面的類爲AC3和AC4。 
        接着咱們去掉對A的引用,加上對B的引用,那麼咱們在這個應用程序下能看到的N1下面的類變成了BC1和BC2,N2下面也同樣。 
        若是咱們同時引用這兩個裝配件,那麼N1下面咱們就能看到四個類:AC一、AC二、BC1和BC2。 
        到這裏,咱們能夠清楚一個概念了,命名空間只是說明一個類型是那個族的,好比有人是漢族、有人是回族;而裝配件代表一個類型住在哪裏,好比有人住在北京、有人住在上海;那麼北京有漢族人,也有回族人,上海有漢族人,也有回族人,這是不矛盾的。 
        上面咱們說了,裝配件是一個類型居住的地方,那麼在一個程序中要使用一個類,就必須告訴編譯器這個類住在哪兒,編譯器才能找到它,也就是說必須引用該裝配件。 
        那麼若是在編寫程序的時候,也許不肯定這個類在哪裏,僅僅只是知道它的名稱,就不能使用了嗎?答案是能夠,這就是反射了,就是在程序運行的時候提供該類型的地址,而去找到它。 
有興趣的話,接着往下看吧。 
三、運行期獲得類型信息有什麼用 
    有人也許疑問,既然在開發時就可以寫好代碼,幹嗎還放到運行期去作,不光繁瑣,並且效率也受影響。 
這就是個見仁見智的問題了,就跟早綁定和晚綁定同樣,應用到不一樣的場合。有的人反對晚綁定,理由是損耗效率,可是不少人在享受虛函數帶來的好處的時侯尚未意識到他已經用上了晚綁定。這個問題說開去,不是三言兩語能講清楚的,因此就點到爲止了。 
    個人見解是,晚綁定可以帶來不少設計上的便利,合適的使用可以大大提升程序的複用性和靈活性,可是任何東西都有兩面性,使用的時侯,須要再三衡量。 
接着說,運行期獲得類型信息到底有什麼用呢? 
仍是舉個例子來講明,不少軟件開發者喜歡在本身的軟件中留下一些接口,其餘人能夠編寫一些插件來擴充軟件的功能,好比我有一個媒體播放器,我但願之後能夠很方便的擴展識別的格式,那麼我聲明一個接口: 
public   interface   IMediaFormat 

    string   Extension   {get;} 
    Decoder   GetDecoder(); 

這個接口中包含一個Extension屬性,這個屬性返回支持的擴展名,另外一個方法返回一個解碼器的對象(這裏我假設了一個Decoder的類,這個類提供把文件流解碼的功能,擴展插件能夠派生之),經過解碼器對象我就能夠解釋文件流。 
那麼我規定全部的解碼插件都必須派生一個解碼器,而且實現這個接口,在GetDecoder方法中返回解碼器對象,而且將其類型的名稱配置到個人配置文件裏面。 
這樣的話,我就不須要在開發播放器的時侯知道未來擴展的格式的類型,只須要從配置文件中獲取如今全部解碼器的類型名稱,而動態的建立媒體格式的對象,將其轉換爲IMediaFormat接口來使用。 
這就是一個反射的典型應用。 

四、如何使用反射獲取類型 
    首先咱們來看如何得到類型信息。 
    得到類型信息有兩種方法,一種是獲得實例對象 
    這個時侯我僅僅是獲得這個實例對象,獲得的方式也許是一個object的引用,也許是一個接口的引用,可是我並不知道它的確切類型,我須要瞭解,那麼就能夠經過調用System.Object上聲明的方法GetType來獲取實例對象的類型對象,好比在某個方法內,我須要判斷傳遞進來的參數是否實現了某個接口,若是實現了,則調用該接口的一個方法: 
… 
public   void   Process(   object   processObj   ) 

    Type   t   =   processsObj.GetType(); 
    if(   t.GetInterface(「ITest」)   !=null   ) 
                    … 

… 
   另一種獲取類型的方法是經過Type.GetType以及Assembly.GetType方法,如: 
              Type   t   =   Type.GetType(「System.String」); 
   須要注意的是,前面咱們講到了命名空間和裝配件的關係,要查找一個類,必須指定它所在的裝配件,或者在已經得到的Assembly實例上面調用GetType。 
        本裝配件中類型能夠只寫類型名稱,另外一個例外是mscorlib.dll,這個裝配件中聲明的類型也能夠省略裝配件名稱(.Net裝配件編譯的時候,默認都引用了mscorlib.dll,除非在編譯的時候明確指定不引用它),好比: 
     System.String是在mscorlib.dll中聲明的,上面的Type   t   =   Type.GetType(「System.String」)是正確的 
     System.Data.DataTable是在System.Data.dll中聲明的,那麼: 
Type.GetType(「System.Data.DataTable」)就只能獲得空引用。 
必須:Type   t   =   Type.GetType("System.Data.DataTable,System.Data,Version=1.0.3300.0,   Culture=neutral,   PublicKeyToken=b77a5c561934e089"); 
     這樣才能夠,你們能夠看下面這個帖子: 
      http://expert.csdn.net/Expert/topic/2210/2210762.xml?temp=.1919977    qqchen的回答很精彩 

五、如何根據類型來動態建立對象 
    System.Activator提供了方法來根據類型動態建立對象,好比建立一個DataTable: 
        Type   t   =   Type.GetType("System.Data.DataTable,System.Data,Version=1.0.3300.0,   Culture=neutral,   PublicKeyToken=b77a5c561934e089"); 
        DataTable   table   =   (DataTable)Activator.CreateInstance(t); 

例二:根據有參數的構造器建立對象 
namespace   TestSpace   { 
    public   class   TestClass 
    { 
          private   string   _value; 
          public   TestClass(string   value)   { 
                _value=value; 
      } 
    } 

… 
Type   t   =   Type.GetType(「TestSpace.TestClass」); 
Object[]   constructParms   =   new   object[]   {「hello」};   //構造器參數 
TestClass   obj   =   (TestClass)Activator.CreateInstance(t,constructParms); 
… 
把參數按照順序放入一個Object數組中便可 

六、如何獲取方法以及動態調用方法 
namespace   TestSpace 

      public   class   TestClass   { 
          private   string   _value; 
          public   TestClass()   { } 
          public   TestClass(string   value)   { 
                _value   =   value; 
          } 
          public   string   GetValue(   string   prefix   )   { 
                if(   _value==null   ) 
                    return   "NULL"; 
                else 
                  return   prefix+"   :   "+_value; 
          } 
            public   string   Value   { 
                set   { 
                    _value=value; 
                } 
                get   { 
                    if(   _value==null   ) 
                            return   "NULL"; 
                    else 
                            return   _value; 
                } 
            } 
      } 

    上面是一個簡單的類,包含一個有參數的構造器,一個GetValue的方法,一個Value屬性,咱們能夠經過方法的名稱來獲得方法而且調用之,如: 
//獲取類型信息 
Type   t   =   Type.GetType("TestSpace.TestClass"); 
//構造器的參數 
object[]   constuctParms   =   new   object[]{"timmy"}; 
//根據類型建立對象 
object   dObj   =   Activator.CreateInstance(t,constuctParms); 
//獲取方法的信息 
MethodInfo   method   =   t.GetMethod("GetValue"); 
//調用方法的一些標誌位,這裏的含義是Public而且是實例方法,這也是默認的值 
BindingFlags   flag   =   BindingFlags.Public   |   BindingFlags.Instance; 
//GetValue方法的參數 
object[]   parameters   =   new   object[]{"Hello"}; 
//調用方法,用一個object接收返回值 
object   returnValue   =   method.Invoke(dObj,flag,Type.DefaultBinder,parameters,null); 
屬性與方法的調用大同小異,你們也能夠參考MSDN 

七、動態建立委託 
    委託是C#中實現事件的基礎,有時候不可避免的要動態的建立委託,實際上委託也是一種類型:System.Delegate,全部的委託都是從這個類派生的 
    System.Delegate提供了一些靜態方法來動態建立一個委託,好比一個委託: 
namespace   TestSpace   { 
      delegate   string   TestDelegate(string   value); 
      public   class   TestClass   { 
            public   TestClass()   { } 
            public   void   GetValue(string   value)   { 
                    return   value; 
            } 
        } 

使用示例: 
TestClass   obj   =   new   TestClass(); 

//獲取類型,實際上這裏也能夠直接用typeof來獲取類型 
Type   t   =   Type.GetType(「TestSpace.TestClass」); 
//建立代理,傳入類型、建立代理的對象以及方法名稱 
TestDelegate   method   =   (TestDelegate)Delegate.CreateDelegate(t,obj,」GetValue」); 
String   returnValue   =   method(「hello」); 

到這裏,咱們簡單的講述了反射的做用以及一些基本的用法,還有不少方面沒有涉及到,有興趣的朋友能夠參考MSDN。 
   很奇怪,不少人都不肯看MSDN,其實你想要的答案,99%均可以在裏面找到。

 

 

 c#中的反射     CSDN Blog推出文章指數概念,文章指數是對Blog文章綜合評分後推算出的,綜合評分項分別是該文章的點擊量,回覆次數,被網摘收錄數量,文章長度和文章類型;滿分100,每個月更新一次。html

c#中的反射 做者: YAOTIEBING
反射的概述  
    
  反射的定義:審查元數據並收集關於它的類型信息的能力。元數據(編譯之後的最基本數據單元)就是一大堆的表,當編譯程序集或者模塊時,編譯器會建立一個類定義表,一個字段定義表,和一個方法定義表等,。System.reflection命名空間包含的幾個類,容許你反射(解析)這些元數據表的代碼  
    
  和反射相關的命名空間(咱們就是經過這幾個命名空間訪問反射信息):  
    
  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  
    
  反射的層次模型:  
    
      
  注:層次間都是一對多的關係  
    
  反射的做用:  
    
  1. 可使用反射動態地建立類型的實例,將類型綁定到現有對象,或從現 有對象中獲取類型  
    
  2. 應用程序須要在運行時從某個特定的程序集中載入一個特定的類型,以便實現某個任務時能夠用到反射。  
    
  3. 反射主要應用與類庫,這些類庫須要知道一個類型的定義,以便提供更多的功能。  
    
  應用要點:  
    
  1. 現實應用程序中不多有應用程序須要使用反射類型  
    
  2. 使用反射動態綁定須要犧牲性能  
    
  3. 有些元數據信息是不能經過反射獲取的  
    
  4. 某些反射類型是專門爲那些clr 開發編譯器的開發使用的,因此你要意識到不是全部的反射類型都是適合每一個人的。  
    
    
    
  反射appDomain 的程序集  
    
   當你須要反射AppDomain 中包含的全部程序集,示例以下:  
   static void Main  
    
   {  
    
   //經過GetAssemblies 調用appDomain的全部程序集  
    
  foreach (Assembly assem in Appdomain.currentDomain.GetAssemblies())  
    
  {  
    
   //反射當前程序集的信息  
    
   reflector.ReflectOnAssembly(assem)  
    
  }  
    
  }  
    
  說明:調用AppDomain 對象的GetAssemblies 方法 將返回一個由System.Reflection.Assembly元素組成的數組。  
    
  反射單個程序集  
    
  上面的方法講的是反射AppDomain的全部程序集,咱們能夠顯示的調用其中的一個程序集,system.reflecton.assembly 類型提供了下面三種方法:  
    
  1. Load 方法:極力推薦的一種方法,Load 方法帶有一個程序集標誌並載入它,Load 將引發CLR把策略應用到程序集上,前後在全局程序集緩衝區,應用程序基目錄和私有路徑下面查找該程序集,若是找不到該程序集系統拋出異常  
    
  2. LoadFrom 方法:傳遞一個程序集文件的路徑名(包括擴展名),CLR會載入您指定的這個程序集,傳遞的這個參數不能包含任何關於版本號的信息,區域性,和公鑰信息,若是在指定路徑找不到程序集拋出異常。  
    
  3. LoadWithPartialName:永遠不要使用這個方法,由於應用程序不能肯定再在載入的程序集的版本。該方法的惟一用途是幫助那些在.Net框架的測試環節使用.net 框架提供的某種行爲的客戶,這個方法將最終被拋棄不用。  
    
  注意:system.AppDomain 也提供了一種Load 方法,他和Assembly的靜態Load 方法不同,AppDomain的load 方法是一種實例方法,返回的是一個對程序集的引用,Assembly的靜態Load 方發將程序集按值封裝發回給發出調用的AppDomain.儘可能避免使用AppDomain的load 方法  
    
    
    
  利用反射獲取類型信息  
    
  前面講完了關於程序集的反射,下面在講一下反射層次模型中的第三個層次,類型反射  
    
  一個簡單的利用反射獲取類型信息的例子:  
    
  using system;  
    
  using sytem.reflection;  
    
  class reflecting  
    
  {  
    
   static void Main(string[]args)  
    
  {  
    
   reflecting reflect=new reflecting();//定義一個新的自身類  
    
   //調用一個reflecting.exe程序集  
    
   assembly myAssembly =assembly.loadfrom(「reflecting.exe」)  
    
   reflect.getreflectioninfo(myAssembly);//獲取反射信息  
    
  }  
    
  //定義一個獲取反射內容的方法  
    
  void getreflectioninfo(assembly myassembly)  
    
  {  
    
   type[] typearr=myassemby.Gettypes();//獲取類型  
    
   foreach (type type in typearr)//針對每一個類型獲取詳細信息  
    
   {  
    
   //獲取類型的結構信息  
    
   constructorinfo[] myconstructors=type.GetConstructors;  
    
   //獲取類型的字段信息  
    
   fieldinfo[] myfields=type.GetFiedls()  
    
   //獲取方法信息  
    
   MethodInfo myMethodInfo=type.GetMethods();  
    
   //獲取屬性信息  
    
   propertyInfo[] myproperties=type.GetProperties  
    
   //獲取事件信息  
    
   EventInfo[] Myevents=type.GetEvents;  
    
    
    
  }  
    
  }  
    
  }  
    
  其它幾種獲取type對象的方法:  
    
  1. System.type 參數爲字符串類型,該字符串必須指定類型的完整名稱(包括其命名空間)  
    
  2. System.type 提供了兩個實例方法:GetNestedType,GetNestedTypes  
    
  3. Syetem.Reflection.Assembly 類型提供的實例方法是:GetType,GetTypes,GetExporedTypes  
    
  4. System.Reflection.Moudle 提供了這些實例方法:GetType,GetTypes,FindTypes  
    
  設置反射類型的成員  
    
   反射類型的成員就是反射層次模型中最下面的一層數據。咱們能夠經過type對象的GetMembers 方法取得一個類型的成員。若是咱們使用的是不帶參數的GetMembers,它只返回該類型的公共定義的靜態變量和實例成員,咱們也能夠經過使用帶參數的GetMembers經過參數設置來返回指定的類型成員。具體參數參考msdn 中system.reflection.bindingflags 枚舉類型的詳細說明。  
    
  例如:  
    
    
    
  //設置須要返回的類型的成員內容  
    
  bindingFlags bf=bingdingFlags.DeclaredOnly|bingdingFlags.Nonpublic|BingdingFlags.Public;  
    
  foreach (MemberInfo mi int t.getmembers(bf))  
    
  {  
    
   writeline(mi.membertype) //輸出指定的類型成員  
    
  }  
    
  經過反射建立類型的實例  
    
  經過反射能夠獲取程序集的類型,咱們就能夠根據得到的程序集類型來建立該類型新的實例,這也是前面提到的在運行時建立對象實現晚綁定的功能  
    
  咱們能夠經過下面的幾個方法實現:  
    
  1. System.Activator 的CreateInstance方法。該方法返回新對象的引用。具體使用方法參見msnd  
    
  2. System.Activator 的createInstanceFrom 與上一個方法相似,不過須要指定類型及其程序集  
    
  3. System.Appdomain 的方法:createInstance,CreateInstanceAndUnwrap,CreateInstranceFrom和CreateInstraceFromAndUnwrap  
    
  4. System.type的InvokeMember實例方法:這個方法返回一個與傳入參數相符的構造函數,並構造該類型。  
    
  5. System.reflection.constructinfo 的Invoke實例方法  
    
  反射類型的接口  
    
  若是你想要得到一個類型繼承的全部接口集合,能夠調用Type的FindInterfaces GetInterface或者GetInterfaces。全部這些方法只能返回該類型直接繼承的接口,他們不會返回從一個接口繼承下來的接口。要想返回接口的基礎接口必須再次調用上述方法。  
    
  反射的性能:  
    
  使用反射來調用類型或者觸發方法,或者訪問一個字段或者屬性時clr 需 要作更多的工做:校驗參數,檢查權限等等,因此速度是很是慢的。因此儘可能不要使用反射進行編程,對於打算編寫一個動態構造類型(晚綁定)的應用程序,能夠採起如下的幾種方式進行代替:  
    
  1. 經過類的繼承關係。讓該類型從一個編譯時可知的基礎類型派生出來,在運行時生成該類 型的一個實例,將對其的引用放到其基礎類型的一個變量中,而後調用該基礎類型的虛方法。  
    
  2. 經過接口實現。在運行時,構建該類型的一個實例,將對其的引用放到其接口類型的一個變量中,而後調用該接口定義的虛方法。  
    
  3.經過委託實現。讓該類型實現一個方法,其名稱和原型都與一個在編譯時就已知的委託相符。在運行時先構造該類型的實例,而後在用該方法的對象及名稱構造出該委託的實例,接着經過委託調用你想要的方法。這個方法相對與前面兩個方法所做的工做要多一些,效率更低一些     
上次在MSDN網站看到一個比較動態調用代碼的文章,用到的例子彷佛比較複雜,爲計算一個複雜多項式子而將其中部分割開,動態造成代碼段來被循環調用。詳細看  .NET下幾種動態生成代碼方式比較。今天看到微軟C#團隊的Eric Gunnerson寫的另一篇關於動態調用代碼性能的比較文章,爲了說明結果和計算的準確性,減小因爲函數複雜而受編譯優化的影響,他使用了一個極爲簡單的例子: 
輸入一個參數,而後返回這個參數加一,這麼簡單的函數,優化和沒有優化的代碼應該不會有差異的了。
      public  class  Processor
      {
        public int Process(int value)
        {
            return value + 1;
        }
    } 


而對比方面,除了上次那幾種外,還加了代理方式調用來進行比較。 
1. 直接調用
 int  value  =  processor.Process(i);
2. 用反射機制,Type.InvokeMember()調用。
     Type t  =  typeof (Processor);
     int  value  =  
        ( int ) t.InvokeMember(
                   " Process " , 
         BindingFlags.Instance  |  BindingFlags.Public  |  
                  BindingFlags.InvokeMethod, 
                   null , processor,  new  object []   {i} );

3. 經過一個接口
      public  interface  IProcessor
     {
        int Process(int value);
    } 

4. 經過一個委託Delegate
      public  delegate  int  ProcessCaller( int  value);
    ProcessCaller processCaller  =  new  ProcessCaller(processor.Process);
     int  value  =  processCaller(i); 

5. 也經過反射機制創建委託再動態調用
     Type delegateType  =  CreateCustomDelegate(methodInfo);
    Delegate p  =  Delegate.CreateDelegate(delegateType, 
                                         process,  " Process " );
     int  value  =  ( int ) p.DynamicInvoke( new  object []   {i} );

6. 元編程方式
對於2和5因爲使用反射機制,不可避免須要創建中間的臨時對象去傳遞參數,將參數和返回值裝箱等操做,所以花費了大量的機器時間。

下面是運行的某次結果(循環100000次):
程序員



結論: 
1.直接調用速度最快是確定的。 
2.接口調用比元編程速度快,而元編程又比委託方式快,但微軟相信Whidbey會極大優化委託調用方式,從而使它接近接口調用的水平。 
3.直接用Type的反射機制是速度最慢的,比用反射機制創建委託來動態調用還慢。 
4.直接使用委託不夠靈活,有時候須要用反射機制創建委託來調用,但會減低性能,但願Whidbey優化了委託的性能後這種狀況能夠改善,靈活是須要犧牲性能的。
 
在實際開發中,咱們常常須要從數據庫中讀取數據並賦值給實體類的相應屬性。在.Text的DataDTOProvider中存在大量這樣的代碼, 好比: 
 public  Role[] GetRoles( int  BlogID)
          {
            System.Collections.ArrayList al=new System.Collections.ArrayList();
            IDataReader reader=DbProvider.Instance().GetRoles(BlogID);
            try
            {
                while(reader.Read())
                {
                    Role role=new Role();
                    if(reader["RoleID"]!=DBNull.Value)
                    {
                        role.RoleID=(int)reader["RoleID"];
                    }
                    if(reader["Name"]!=DBNull.Value)
                    {
                        role.Name=(string)reader["Name"];
                    }
                    if(reader["Description"]!=DBNull.Value)
                    {
                        role.Description=(string)reader["Description"];
                    }
                    //ReaderToObject(reader,role);
                    al.Add(role);
                }
            }
            finally
            {
                reader.Close();
            }
            return (Role[])al.ToArray(typeof(Role));
        
        }

對於上面的代碼,我以爲有幾點不優雅之處: 
一、每次對Role的屬性進行賦值時,都要檢查reader的值是否爲DBNull,出現了不少重複代碼 
二、每次對Role的屬性進行賦值時,都要進行類型轉換, 而Role屬性的類型是已知的,是否是能夠自動完成這樣的轉換? 
三、每次對Role的屬性進行賦值時,都要進行Role屬性與數據庫字段的對應。若是咱們在設計數據庫與實體類時,保證數據庫字段與實體類屬性採用一樣的名稱,那利用反射,咱們能夠經過代碼自動進行屬性與字段的對應。即便數據庫字段與屬性不一樣名,咱們也能夠經過更改查詢語句,來作到這一點。 
是否是能夠對上面的代碼進行改進,使代碼變得更優雅?那優雅的代碼應該是什麼樣的呢?若是咱們用上面代碼中註釋的代碼行ReaderToObject(reader,role);取代它以前的對Role屬性進行賦值的語句,是否是會使代碼變得更優雅?ReaderToObject的做用就是自動完成將reader中的值寫入到role中對應的屬性中(前提是reader中的字段與role中對應的屬性具備相同的名稱)。如今咱們的任務就是實現ReaderToObject, 有了強大的武器—Reflection,咱們的任務就變得很輕鬆, 也很少說了,下面的代碼是個人實現方法:
 private  void  ReaderToObject(IDataReader reader, object  targetObj)
          {
            for(int i=0;i<reader.FieldCount;i++)
            {
                System.Reflection.PropertyInfo propertyInfo=targetObj.GetType().GetProperty(reader.GetName(i));
                if(propertyInfo!=null)
                {
                    if(reader.GetValue(i)!=DBNull.Value)
                    {
                        propertyInfo.SetValue(targetObj,reader.GetValue(i),null);
                    }
                }
            }
        }
ReaderToObject能夠將reader中的數據讀入到任何實體類中。數據庫字段與實體類屬性的映射原則是名稱相同。固然,咱們也能夠經過配置文件來進行二者映射。 

    我的想法:在開發中,面對那麼多設計思想和設計模式,經常使人感到迷惑,當你把更多的精力放在選用哪一個設計思想或設計模式時,我以爲不要忽略很重要的一點,儘量地減小重複代碼,只要咱們能有效地減小重複代碼,咱們採用的方法就是好方法,而不要太在意採用了哪一種模式。就像獨孤九劍,正由於擺脫了傳統招式的束縛,才能望風披靡!
先來看這段 NUnit 測試代碼,咱們但願用反射機制在運行時訪問一個對象的枚舉類型的域或屬性:
 
[TestFixture] 
public class PaymentInfo 

  public enum PaymentType 
 { 
    Cash, CreditCard, Check 
 } 

  public PaymentType Type; 

  public void Test() 
 { 
    PaymentInfo payment =  new PaymentInfo(); 
    payment.Type = PaymentType.Cash; 

    System.Reflection.FieldInfo enumField = GetType().GetField("Type"); 

     int paymentTypeInt32; 

    paymentTypeInt32 = ( int)enumField. GetValue(payment); 
    Assert.AreEqual(( int)PaymentType.Cash, paymentTypeInt32); 

    enumField. SetValue (payment,  paymentTypeInt32 );
    Assert.AreEqual(PaymentType.Cash, payment.Type); 
 } 
}
 
 
在這個測試中,使之經過的辦法其實很是簡單:把劃線部分 強制轉換 爲枚舉類型便可,如: (PaymentType)paymentTypeInt32 。可問題是:在運行時如何 動態轉換 類型呢?好比說我在寫 ElegantDAL 的時候,須要將從數據庫讀出的一個類型爲 int 的數值寫入到要返回的對象的一個枚舉型字段中,此時我只有 fieldInfo 、 columnValue 和 resultObject ,然而寫成 fieldInfo.SetValue(resultObject, columnValue) 就會出現前面提到的錯誤,但是我又只有一個運行時的 Type 信息( fieldInfo.FieldType ),我又不能寫成fieldInfo.SetValue(resultObject, (fieldInfo.FieldType)columnValue) ……
 
只好將這種狀況列爲一個特例處理,而咱們的救兵則是 Enum.ToObject() 方法——你知道有更好的方法解決這個問題嗎?
 
BTW:  不少朋友寫信來問 偶承諾的下一篇文章何時才能搞定,其實我也不想拖,可最近工做確實比較緊張,而個人最佳寫做時間又都在深更半夜(以前還要熱身進入狀態),因此遲遲不能結稿。目前其實已經寫了好多(已經和 第一篇長度差很少了),但是發現要寫的內容還太多(也許是我寫得太細了,由於個人目標不只是講 how ,更多的是 what 和 why ),因此如今決定把本篇文字再細分爲兩部分(上篇只講TP/RP+MBR 對象;把 CBO 相關的內容放到下篇再寫),以便可以儘快讓你們看到新的內容——嗯,爭取週五 release 吧。至於還有朋友但願我可以深刻寫些關於  ElegantDAL 的設計與實現細節,這個留待稍後吧,正好咱們將要整理的項目文檔中也要有這一塊內容(如今在咱們這個企業級項目中用得還真挺好),到時候一塊兒寫啦(但願可以和 canyue 儘快完成第三版本的重寫工做,目前使用的是第二版)。
實際上運行測試時發如今標紅的這行上拋出一個異常:「對象類型沒法轉換爲目標類型」。究其緣由,原來是由於 CLR 的反射機制不容許枚舉類型與整數類型之間隱式轉換。不過 C# 編譯器仍是容許咱們經過強制類型轉換的語法來進行二者間的顯式轉換。
相關文章
相關標籤/搜索