C# 利用反射動態創建對象——帶參數的構造函數和String類型


最近筆者有一個想法需要利用反射動態創建對象(如string,int,float,bool,以及自定義類等)來實現,一直感覺反射用不好,特別是當構造函數帶參數的時候。
MSDN上給出的例子十分復雜,網上的帖子則一般都說很簡單,那就看看網上比較普遍的說法:

“反射”其實就是利用程序集的元數據信息。 

反射可以有很多方法,編寫程序時請先導入 System.Reflection 命名空間,假設你要反射一個 DLL 中的類,並且沒有引用它(即未知的類型): 
Assembly assembly = Assembly.LoadFile("程序集路徑,不能是相對路徑"); // 加載程序集(EXE 或 DLL) 
object obj = assembly.CreateInstance("類的完全限定名(即包括命名空間)"); // 創建類的實例 

若要反射當前項目中的類可以為: 

Assembly assembly = Assembly.GetExecutingAssembly(); // 獲取當前程序集 
object obj = assembly.CreateInstance("類的完全限定名(即包括命名空間)"); // 創建類的實例,返回為 object 類型,需要強制類型轉換 

也可以為: 
Type type = Type.GetType("類的完全限定名"); 
object obj = type.Assembly.CreateInstance(type); 
反射創建類的實例

因為這段描述在很多地方都有看到,筆者也不知道原始出處,所以這里就給出筆者第一次看到的地方:http://hi.baidu.com/rayord/item/92e58ddb0d34c13de3108fbb

上述描述中提到的三種方法其實都是大同小異的,核心就是通過System.Reflection.Assembly 類型的CreateInstance方法創建實例。

關於System.Reflection.Assembly 類可以直接在MSDN上查詢詳細信息http://msdn.microsoft.com/zh-cn/library/system.reflection.assembly(v=vs.110).aspx

那么簡單的解釋一下這種方法的原理:

1.找到要實例化的類所在的程序集,並將之實例為System.Reflection.Assembly 類的對象

2.利用System.Reflection.Assembly 類提供的CreateInstance方法,創建類的對象

看起來確實很簡單,只是這種方法真的好用么?

筆者進行了測試以說明:

第一次測試,創建一個簡單的自定義類型對象

首先創建一個類:

class Test
{
    private string _strId;
    public string ID
    {
        get { return _strId; }
        set { _strId = value; }
    }
    
    public Test()
    {
    }
}
View Code

然后在主函數中加入代碼:

Assembly assembly = Assembly.GetExecutingAssembly(); // 獲取當前程序集 
object obj = assembly.CreateInstance("ReflectionTest.Test"); //類的完全限定名(即包括命名空間)

調試結果:顯示obj對象的確不為空,證明這種方法可行。

第二次測試,加深難度,測試類的構造函數需要傳遞參數
首先修改Test類,將其構造函數改為:

public Test(string str)
{
     _strId = str;
}

調試結果:直接拋出異常:未找到類型“ReflectionTest.Test”上的構造函數。這是因為CreateInstance方法默認情況下是通過找無參數的構造函數去創建對象的,現在找不到當然會出錯,實時上CreateInstance方法提供了3中簽名,其中有CreateInstance(String, Boolean, BindingFlags, Binder, Object [], CultureInfo, Object []) 就可以滿足這種情況:

修改主函數如下:

Assembly assembly = Assembly.GetExecutingAssembly(); // 獲取當前程序集 
//object obj = assembly.CreateInstance("ReflectionTest.Test"); //類的完全限定名(即包括命名空間)
object[] parameters = new object[1];
parameters[0] = "test string";
object obj = assembly.CreateInstance("ReflectionTest.Test",true,System.Reflection.BindingFlags.Default,null,parameters,null,null);// 創建類的實例 
View Code

調試結果:正常,並且對象中變量值也是正確的,但是這離筆者的需求還差很遠。繼續
第三次測試,繼續加深難度,創建string的對象

首先知道string是System.String的別名,所以要創建的是System.String的對象,而System.String在mscorlib.dll中,所以需要將mscorlib.dll實例為System.Reflection.Assembly的對象,這里利用System.Type類型的屬性Assembly來實現功能。

System.String的構造函數有很多種,本文中筆者就不墨跡了,采用String( Char []) 。

最終將主函數中代碼改為:

Type type = Type.GetType("System.String");
object[] parameters = new object[1];
char[] lpChar = { 't','e','s','t' };
parameters[0] = lpChar;

object obj = type.Assembly.CreateInstance("ReflectionTest.Test",true,System.Reflection.BindingFlags.Default,null,parameters,null,null);// 創建類的實例 
View Code

調試結果:對象為空,失敗了,事實上這種方法還有個問題,如將Test類構造函數修改為

public Test(string str)
{
    ID = str;//屬性賦值
}
View Code

調試結果:對象創建成功,但是變量為空

以上問題詳細原因筆者現在也無法解釋,正在查找相關資料。

解決方案
最后筆者在《C#中使用反射獲取結構體實例及思路》一文中找到了解決方案

采用System.Activator 類的CreateInstance方法。

最后見代碼:

Type type = Type.GetType("System.String");
object[] parameters = new object[1];
char[] lpCh = { 't', 'e', 's', 't' };
parameters[0] = lpCh;

object obj = Activator.CreateInstance(type, parameters);
View Code

調試結果:對象創建成功,且變量值正常

結論

采用System.Activator 類的CreateInstance方法,要比System.Reflection.Assembly的CreateInstance簡單有效很多。有興趣的朋友可以仔細看看。詳細資料http://msdn.microsoft.com/zh-cn/library/system.activator(v=vs.110).aspx


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM