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.spa

關於System.Reflection.3d

那麼簡單的解釋一下這種方法的原理:調試

1.找到要實例化的類所在的程序集,並將之實例爲System.Reflection.code

2.利用System.Reflection.對象

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

筆者進行了測試以說明:get

 

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

首先建立一個類:

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

而後在主函數中加入代碼:

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);// 建立類的實例 

調試結果:正常,而且對象中變量值也是正確的,可是這離筆者的需求還差很遠。繼續

 

第三次測試,繼續加深難度,建立string的對象

首先知道string是System.String的別名,因此要建立的是System.String的對象,而System.String在mscorlib.dll中,因此須要將mscorlib.dll實例爲System.Reflection.

System.String的構造函數有不少種,本文中筆者就不墨跡了,採用String(

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

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);// 建立類的實例 

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

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

調試結果:對象建立成功,可是變量爲空

以上問題詳細緣由筆者如今也沒法解釋,正在查找相關資料。

 

解決方案

採用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);

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

 

結論

採用System.Activator 類的CreateInstance方法,要比System.Reflection.Assembly的CreateInstance簡單有效不少。有興趣的朋友能夠仔細看看。

 

 

補充知識點

使用Type.GetType(string typeName)方法獲取類型時,參數typeName中聲明的程序集沒有必要在當前項目中引用,只要放在生成項目的bin目錄下便可:

好比下面我構造了一個類庫項目MyClass包含類MyClassObject

public class MyClassObject
{
}

 

我在項目ConsoleApplication2中並無引用類庫項目MyClass的程序集,以下圖所示

可是我手動將類庫項目MyClass的DLL文件複製到了項目ConsoleApplication2的bin目錄下:

而後我在項目ConsoleApplication2中使用:

Type myType = Type.GetType("MyClass.MyClassObject,MyClass");

是能夠成功獲取到MyClassObject的Type類型的:

這說明C#中使用反射,在項目中不引用程序集也能夠構造程序集中的類,只要將程序集文件(DLL文件)放在和項目所生成文件相同的文件夾(通常是bin目錄)下便可。

相關文章
相關標籤/搜索