摘自:http://hi.baidu.com/yangyuhang/blog/item/f12ea90e13f214e336d12250.html
在VS.Net中,有很多種方法動(dòng)態(tài)調(diào)用對象的構(gòu)造函數(shù)。一是通過Activator類的CreateInstance()方法。這個(gè)方法我們在Remoting中也用過。它實(shí)際上是在本地或從遠(yuǎn)程創(chuàng)建對象類型,或獲取對現(xiàn)有遠(yuǎn)程對象的引用。它的方法簽名是:public static object CreateInstance(Type);(還有其他重載方法)注意它的返回值為object,MSDN對返回值的描述是:對新創(chuàng)建對象的引用。
二是通過Assembly類的方法CreateInstance()。方法名和前一樣,不過它不是靜態(tài)方法。Assembly是在System.Reflection命名空間中。方法簽名:public object CreateInstance(Type);(同樣還有其他重載方法)返回值仍然是object,MSDN對返回值的描述是:表示該類型的 Object 的實(shí)例,其區(qū)域性、參數(shù)、聯(lián)編程序和激活屬性設(shè)置為空引用(Visual Basic 中為 Nothing),并且 BindingFlags 設(shè)置為 Public 或 Instance,或者設(shè)置為空引用 (Nothing)(如果沒有找到 typeName)。
當(dāng)然還有其他方法,例如通過MethodInfo獲得方法信息后,根據(jù)IsConstructor屬性,判斷是否構(gòu)造函數(shù),再根據(jù)GetParamters()方法獲得參數(shù),最后通過Invoke()方法來調(diào)用,等等……。大家可以參考MSDN。
在這里,我且把問題簡單化,只調(diào)用其默認(rèn)構(gòu)造函數(shù)。通過CreateInstance()方法獲得object對象,再轉(zhuǎn)換為實(shí)際的自定義對象類型。事實(shí)證明,這種轉(zhuǎn)換為出現(xiàn)異常。根本的原因我還弄不清楚,初步的猜測,對于動(dòng)態(tài)加載的assembly,和手動(dòng)添加的assembly,F(xiàn)ramework將兩者視為了不同的assembly,即使我們使用的是同一個(gè)DLL。
我也注意到Actovator.CreateInstance()返回的是新創(chuàng)建對象的引用。是否是引用再作怪呢?但Assembly.CreateInstance()方法,根據(jù)MSDN的描述,返回的是object實(shí)例,然而仍然會(huì)拋出同樣的異常。所以,出現(xiàn)問題的具體原因,我確實(shí)無法解釋。
確實(shí)VS.Net博大精深,很多內(nèi)在的運(yùn)行機(jī)制我們不得而知。好吧,我們就知其然而不知其所以然吧,至少我現(xiàn)在已經(jīng)知道了利用反射動(dòng)態(tài)創(chuàng)建對象的解決之道!管它這么多,只要會(huì)用就行,退而求其次,也未嘗不可。
利用反射動(dòng)態(tài)創(chuàng)建對象,事實(shí)上就是通過Assembly動(dòng)態(tài)加載DLL。這里所謂的“對象”,應(yīng)分為兩種類型。不同的類型,解決的辦法也不相同。
一、.Net自身提供的類對象,例如Form對象、Control對象。
這也是我們在程序開發(fā)中會(huì)經(jīng)常用到的。一般我們開發(fā)應(yīng)用程序,都是將界面定義好。有多少個(gè)窗口,有多少個(gè)控件,事先做好,放在項(xiàng)目中。但有時(shí)作交互設(shè)計(jì)時(shí),還需要考慮用戶的請求。也許用戶希望某些窗體能夠自己決定加載的時(shí)間。也就是說,需要提供運(yùn)行時(shí)加載的功能。這時(shí),就需要通過反射來動(dòng)態(tài)創(chuàng)建對象了。(加載的窗體對象dll,通常是放在配置文件中。在.Net中,有專門的配置文件,它是xml格式。有關(guān)配置文件,我希望能專門寫一篇文章。在本文,我的例子是固定的加載程序集。) 1、創(chuàng)建要?jiǎng)討B(tài)加載的窗體對象
首先,創(chuàng)建一個(gè)窗體對象FirstForm,這個(gè)窗體只有一個(gè)控件Lable,來顯示窗體的名稱。然后我們將它編譯為dll文件FirstForm.dll,放在e:/AutoForm中。(要生成Dll文件,而不是exe,請?jiān)赟olution Explorer(解決方案資源管理器)中的 FirstForms 項(xiàng)目上單擊鼠標(biāo)右鍵,選擇 PRoperties(屬性)。在 Output Type(輸出類型)組合框中選擇 Class Library(類庫)。)
這個(gè)對象的程序集名為FirstForm.dll,類型為FirstForm.Form1。
2、創(chuàng)建應(yīng)用程序,動(dòng)態(tài)加載該對象
啟動(dòng)一個(gè)新的 Windows 窗體項(xiàng)目。將其命名為 AutoLoadForm。在新項(xiàng)目中包含的空窗體 Form1 中,將它的IsMdiContainer屬性更改為True。這樣,該窗體即變成一個(gè) MDI 父窗體。更改窗體的大小,使窗體的長和寬的尺寸大約為默認(rèn)值的兩倍。
將一個(gè)面板控件拖動(dòng)到窗體上,然后設(shè)置它的Dock屬性,使它靠接在窗體的頂部。更改面板的大小,使它的高度大約為 50px。
將一個(gè)組合框拖動(dòng)到面板上。將它命名為 cboForms,然后將它的DropDownStyle設(shè)置為DropDownList。
最后,將一個(gè)按鈕拖動(dòng)到面板上。將它命名為 btnLoadForm,然后將它的Text屬性設(shè)置為LoadForm。
此時(shí),F(xiàn)orm1 應(yīng)如圖 1 所示。
然后為程序添加命名空間: using System.Reflecton;
單擊btnLoadForm控件,寫入以下代碼:
private void btnLoadForm_Click(object sender, System.EventArgs e)

{
Assembly assembly = Assembly.LoadFrom(@"e:/AutoFormFirstForm.dll");
Type type = assembly.GetType("FirstForm.Form1");
object obj = Activator.CreateInstance(type);
Form formToShow = (Form)obj;
formToShow.MdiParent = this;
formToShow.Show();
}代碼說明: 1)首先是通過Assembly.LoadFrom()來加載dll文件; 2)再通過GetType()來獲得要?jiǎng)?chuàng)建的Form類對象的類型。注意,在GetType()方法的參數(shù)為類型的名字,為string類型,同時(shí)該名字應(yīng)為類型的FullName,即:命名空間名.類名; 3)然后通過Activator.CreateInstance()方法創(chuàng)建該類型對象,返回object對象。 4)再將該對象強(qiáng)制轉(zhuǎn)換為Form類型。 5)最后調(diào)用即可。
運(yùn)行程序,單擊按鈕,結(jié)果如下:
結(jié)論:可以看到,對于.Net自身提供的類對象,我們對它直接強(qiáng)制轉(zhuǎn)換即可。不會(huì)出現(xiàn)任何問題。
二、自定義對象
前面已經(jīng)說過,對于自定義的對象,進(jìn)行強(qiáng)制轉(zhuǎn)換會(huì)拋出異常。因此,我們需要做些變通才行。
我們說,動(dòng)態(tài)加載的Dll和手工添加的dll引用,系統(tǒng)會(huì)認(rèn)為不是同一個(gè)Assembly。那么應(yīng)該怎么解決?想一想,對了,應(yīng)該使用接口。但是,這里使用接口的方法稍微有點(diǎn)特殊。還是先按步驟來講解吧。 1、創(chuàng)建一個(gè)接口,該接口包括要加載對象類的方法、屬性等:
新建一個(gè)“類庫”項(xiàng)目,取名為AutoObjectInterface:
using System;
namespace AutoObjectInterface

{
public interface IAutoObject

{
void Print(string s);
}
}這個(gè)接口很簡單,只是提供一個(gè)Print()方法而已。
然后將它編譯為Dll文件,名為AutoObjectInterface。
2、創(chuàng)建自定義類對象:
新建一個(gè)“類庫”項(xiàng)目,取名為AutoObject,添加前面創(chuàng)建的接口Dll引用:
using System;
namespace AutoObject

{
public class TestObject:AutoObjectInterface.IAutoObject

{
public TestObject()

{
}
public void Print(string s)

{
Console.WriteLine(s);
}
}
}這個(gè)類實(shí)現(xiàn)了第一步創(chuàng)建的接口。注意,這里實(shí)現(xiàn)的接口不是直接寫在該類中,而是獨(dú)立的Dll。在這個(gè)類中,是添加了該接口的dll,然后再實(shí)現(xiàn)它。這就是前面說的使用接口的一點(diǎn)特殊性。為什么要這樣,是因?yàn)楹竺鎰?dòng)態(tài)加載時(shí),也要引用該接口Dll。我們動(dòng)態(tài)創(chuàng)建后的對象,正是轉(zhuǎn)換為該接口對象。由于實(shí)際的類和動(dòng)態(tài)創(chuàng)建的類都引用并實(shí)現(xiàn)了該接口Dll,因此它的轉(zhuǎn)換才能成功。這正是實(shí)現(xiàn)的關(guān)鍵!也許有人疑問:我們能否將接口就放在要?jiǎng)?chuàng)建的類中,然后實(shí)現(xiàn)它。編譯成dll文件,然后動(dòng)態(tài)加載該dll,同時(shí)也手動(dòng)添加該dll。動(dòng)態(tài)創(chuàng)建后的對象,再強(qiáng)制轉(zhuǎn)換為這個(gè)接口類型,不可以嗎?答案當(dāng)然是否定的,為什么?別問我,我也不知道!總之,我現(xiàn)在講的方法,才是通過反射動(dòng)態(tài)創(chuàng)建自定義對象的不二法門!!
也許會(huì)有人說我太武斷!如果你不信,去試試。如果用另外的方法能成功,我一定改正錯(cuò)誤。至少現(xiàn)在我能這樣武斷。
言歸正傳。現(xiàn)在我們再將給類編譯為dll。名為AutoObject.dll,放到e:/NewObject中。
3、利用反射動(dòng)態(tài)創(chuàng)建該對象:
新建一個(gè)控制臺(tái)項(xiàng)目,取名為StudyReflection,添加前面創(chuàng)建的接口Dll引用。代碼如下:AutoObjectInterface.IAutoObject
Obj = (AutoObjectInterface.IAutoObject)obj;
using System;
using System.Reflection;

namespace studyReflection

{
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注