C#反射的Assembly的簡單應用

世紀緣發表於2016-09-04
 反射(Reflection)是.NET中的重要機制,通過反射,可以在執行時獲得.NET中每一個型別(包括類、結構、委託、介面和列舉等)的成員,包括方法、屬性、事件,以及建構函式等。還可以獲得每個成員的名稱、限定符和引數等。有了反射,即可對每一個型別瞭如指掌。如果獲得了建構函式的資訊,即可直接建立物件,即使這個物件的型別在編譯時還不知道。

Assembly就是反應反射的一種應用,它定義和載入程式集,載入在程式集清單中列出模組,以及從此程式集中查詢型別並建立該型別的例項。簡單地說就是,使用Assembly在程式中你不用事先寫比如下面的東西了:

[csharp] view plain copy
 print?
  1. PersonClass person = new PersonClass();  
  2. person.Method();  

 你只要知道PersonClass這個類的程式集,名稱空間和類名直接使用反射就可以使用。你只需要這樣寫:

[html] view plain copy
 print?
  1. PersonClass person;  
  2. person =   
  3. person = (PersonClass)(Assembly.Load("程式集").CreateInstance("名稱空間.類名", false, BindingFlags.Default, null, args, null, null));  
  4. person.Method();  

下面用一個小例子來看看Assembly應用的方便性。

需求:有幾種檔案格式,字尾分別是.One,.Two,.Three,... 有很多種,後續還可能增加。這些檔案的格式都不一樣,也就是說讀取方式就不一樣。那麼根據傳入的檔案字尾和路徑讀出檔案的內容。

實現:

這種需求的特點是,根據選擇做不同的處理,但是都是出的一種結果,那麼可以使用簡單工廠模式來完成。

讀取檔案有一個父類FileSuper,內部如下:

[csharp] view plain copy
 print?
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Text;  
  4.   
  5. namespace reflect  
  6. {  
  7.     public abstract class FileSuper//獲取不同字尾名檔案的內容  
  8.     {  
  9.        public abstract string GetFileContext(string fileFullPath);  
  10.     }  
  11. }  

分別有MyFileOne,MyFileTwo,MyFileThree等,繼承FileSuper,如下:

[csharp] view plain copy
 print?
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Text;  
  4.   
  5. namespace reflect  
  6. {  
  7.     public class MyFileOne : FileSuper  
  8.     {  
  9.         public override string GetFileContext(string fileFullPath)  
  10.         {  
  11.             return "One型別檔案的內容";  
  12.         }  
  13.     }  
  14. }  
[csharp] view plain copy
 print?
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Text;  
  4.   
  5. namespace reflect  
  6. {  
  7.     public class MyFileTwo : FileSuper  
  8.     {  
  9.         public override string GetFileContext(string fileFullPath)  
  10.         {  
  11.             return "Two型別檔案的內容";  
  12.         }  
  13.     }  
  14. }  
[csharp] view plain copy
 print?
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Text;  
  4.   
  5. namespace reflect  
  6. {  
  7.     public class MyFileThree : FileSuper  
  8.     {  
  9.         public override string GetFileContext(string fileFullPath)  
  10.         {  
  11.             return "Three型別檔案的內容";  
  12.         }  
  13.     }  
  14. }  

一個工廠類根據字尾名決定例項化哪個類:

[csharp] view plain copy
 print?
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Text;  
  4.   
  5. namespace reflect  
  6. {  
  7.     public class OperationFile  
  8.     {  
  9.         static FileSuper fileSuper = null;  
  10.   
  11.         public static string GetStringByFile(string fileFullPath, string extendName)  
  12.         {  
  13.             switch (extendName)  
  14.             {  
  15.                 case "One":  
  16.   
  17.                     fileSuper = new MyFileOne();  
  18.   
  19.                     break;  
  20.   
  21.                 case "Two":  
  22.   
  23.                     fileSuper = new MyFileTwo();  
  24.   
  25.                     break;  
  26.   
  27.                 case "Three":  
  28.   
  29.                     fileSuper = new MyFileThree();  
  30.   
  31.                     break;  
  32.             }  
  33.   
  34.             if (fileSuper != null)  
  35.             {  
  36.                 return fileSuper.GetFileContext(fileFullPath);  
  37.             }  
  38.   
  39.             return "沒有指定的型別";  
  40.         }  
  41.     }  
  42. }  

客戶端呼叫,顯示結果:

[csharp] view plain copy
 print?
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Text;  
  4.   
  5. namespace reflect  
  6. {  
  7.     public class Program  
  8.     {  
  9.         static void Main(string[] args)  
  10.         {  
  11.             string fileContext = OperationFile.GetStringByFile("路徑""One");  
  12.   
  13.             Console.WriteLine(fileContext);  
  14.   
  15.             Console.ReadLine();  
  16.         }  
  17.     }  
  18. }  

這樣解決了這個需求,前面在讀書筆記6:工廠方法模式 中提到了這種方式的缺點,就是不符合開放封閉原則,那麼如何改進了,除了工廠方法模式,我們可以使用Assembly。使用它之前,要先寫一個類和一個配置檔案。

先看配置檔案:MyFile.xml

[html] view plain copy
 print?
  1. <?xml version="1.0" encoding="utf-8" ?>  
  2. <FileExtendName>  
  3.   <extend>  
  4.     <name>One</name>  
  5.     <class>MyFileOne</class>  
  6.   </extend>  
  7.   <extend>  
  8.     <name>Two</name>  
  9.     <class>MyFileTwo</class>  
  10.   </extend>  
  11.   <extend>  
  12.     <name>Three</name>  
  13.     <class>MyFileThree</class>  
  14.   </extend>  
  15. </FileExtendName>  

是字尾名和類名的對應。

另一個讀取配置檔案的類ExtendNameDataTable。

[csharp] view plain copy
 print?
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Text;  
  4. using System.Data;  
  5.   
  6. namespace reflect  
  7. {  
  8.     public class ExtendNameDataTable  
  9.     {  
  10.         private static DataSet extendDataSet;  
  11.   
  12.         public static DataSet ExtendDataSet  
  13.         {  
  14.             get  
  15.             {  
  16.                 if (extendDataSet == null)  
  17.                 {  
  18.                     extendDataSet = new DataSet();  
  19.   
  20.                     extendDataSet.ReadXml(@"F:\MyFile.xml");  
  21.                 }  
  22.                 return extendDataSet;  
  23.             }  
  24.         }  
  25.     }  
  26. }  

做好這兩個準備後,只需修改OperationFile工廠類,其餘都不用修改。使用Assembly來根據配置檔案,自動按照傳入的字尾名載入類,並且例項化,修改後的OperationFile如下:

[csharp] view plain copy
 print?
  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Text;  
  4. using System.Data;  
  5. using System.Reflection;  
  6.   
  7. namespace reflect  
  8. {  
  9.     public class OperationFile  
  10.     {  
  11.   
  12.         public static string GetStringByFile(string fileFullPath, string extendName)  
  13.         {  
  14.   
  15.             DataRow dr = ((DataRow[])ExtendNameDataTable.ExtendDataSet.Tables[0].Select("name='" + extendName + "'"))[0];  
  16.   
  17.             object[] args = null;  
  18.   
  19.             FileSuper fileSuper;  
  20.   
  21.             fileSuper = (FileSuper)(Assembly.Load("reflect").CreateInstance(  
  22.   
  23.                 "reflect." + dr["class"].ToString(), false, BindingFlags.Default, null, args, nullnull));  
  24.   
  25.             return fileSuper.GetFileContext(fileFullPath);  
  26.   
  27.         }  
  28.     }  
  29. }  

客戶端呼叫不變輸出結果:

我們看到,這樣一來,如果有了新的檔案結構,只需要再寫一個MyFileFour類繼承自FileSuper;然後再在MyFile.xml中增加相應的對應關係就可以了,避免了要修改OperationFile的case分支,符合開放封閉原則。

    當然Assembly這麼好使用,也不是所有情況下都能用的,當在迴圈中碰到了這種情況,那麼還是使用簡單工廠模式或者工廠方法模式吧,因為再迴圈中使用Assembly例項化會導致效能下降。

相關文章