在VS.Net中,有很多种方法动态调用对象的构造函数。一是通过Activator类的CreateInstance()方法。这个方法我们在Remoting中也用过。它实际上是在本地或从远程创建对象类型,或获取对现有远程对象的引用。它的方法签名是:public static object CreateInstance(Type);(还有其他重载方法)注意它的返回值为object,MSDN对返回值的描述是:对新创建对象的引用。
二是通过Assembly类的方法CreateInstance()。方法名和前一样,不过它不是静态方法。Assembly是在System.Reflection命名空间中。方法签名:public object CreateInstance(Type);(同样还有其他重载方法)返回值仍然是object,MSDN对返回值的描述是:表示该类型的 Object 的实例,其区域性、参数、联编程序和激活属性设置为空引用(Visual Basic 中为 Nothing),并且 BindingFlags 设置为 Public 或 Instance,或者设置为空引用 (Nothing)(如果没有找到 typeName)。
当然还有其他方法,例如通过MethodInfo获得方法信息后,根据IsConstructor属性,判断是否构造函数,再根据GetParamters()方法获得参数,最后通过Invoke()方法来调用,等等……。大家可以参考MSDN。
在这里,我且把问题简单化,只调用其默认构造函数。通过CreateInstance()方法获得object对象,再转换为实际的自定义对象类型。事实证明,这种转换为出现异常。根本的原因我还弄不清楚,初步的猜测,对于动态加载的assembly,和手动添加的assembly,Framework将两者视为了不同的assembly,即使我们使用的是同一个DLL。
我也注意到Actovator.CreateInstance()返回的是新创建对象的引用。是否是引用再作怪呢?但Assembly.CreateInstance()方法,根据MSDN的描述,返回的是object实例,然而仍然会抛出同样的异常。所以,出现问题的具体原因,我确实无法解释。
确实VS.Net博大精深,很多内在的运行机制我们不得而知。好吧,我们就知其然而不知其所以然吧,至少我现在已经知道了利用反射动态创建对象的解决之道!管它这么多,只要会用就行,退而求其次,也未尝不可。
利用反射动态创建对象,事实上就是通过Assembly动态加载DLL。这里所谓的“对象”,应分为两种类型。不同的类型,解决的办法也不相同。
一、.Net自身提供的类对象,例如Form对象、Control对象。
这也是我们在程序开发中会经常用到的。一般我们开发应用程序,都是将界面定义好。有多少个窗口,有多少个控件,事先做好,放在项目中。但有时作交互设计时,还需要考虑用户的请求。也许用户希望某些窗体能够自己决定加载的时间。也就是说,需要提供运行时加载的功能。这时,就需要通过反射来动态创建对象了。(加载的窗体对象dll,通常是放在配置文件中。在.Net中,有专门的配置文件,它是xml格式。有关配置文件,我希望能专门写一篇文章。在本文,我的例子是固定的加载程序集。) 1、创建要动态加载的窗体对象
首先,创建一个窗体对象FirstForm,这个窗体只有一个控件Lable,来显示窗体的名称。然后我们将它编译为dll文件FirstForm.dll,放在e:/AutoForm中。(要生成Dll文件,而不是exe,请在Solution Explorer(解决方案资源管理器)中的 FirstForms 项目上单击鼠标右键,选择 Properties(属性)。在 Output Type(输出类型)组合框中选择 Class Library(类库)。)
这个对象的程序集名为FirstForm.dll,类型为FirstForm.Form1。
2、创建应用程序,动态加载该对象
启动一个新的 Windows 窗体项目。将其命名为 AutoLoadForm。在新项目中包含的空窗体 Form1 中,将它的IsMdiContainer 属性更改为 True。这样,该窗体即变成一个 MDI 父窗体。更改窗体的大小,使窗体的长和宽的尺寸大约为默认值的两倍。
将一个面板控件拖动到窗体上,然后设置它的 Dock 属性,使它靠接在窗体的顶部。更改面板的大小,使它的高度大约为 50px。
将一个组合框拖动到面板上。将它命名为 cboForms,然后将它的 DropDownStyle 设置为 DropDownList。
最后,将一个按钮拖动到面板上。将它命名为 btnLoadForm,然后将它的 Text 属性设置为 Load Form。