摘自:http://hi.baidu.com/yangyuhang/blog/item/f12ea90e13f214e336d12250.html

在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

此时,Form1 应如图 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()来获得要创建的Form类对象的类型。注意,在GetType()方法的参数为类型的名字,为string类型,同时该名字应为类型的FullName,即:命名空间名.类名; 3)然后通过Activator.CreateInstance()方法创建该类型对象,返回object对象。 4)再将该对象强制转换为Form类型。 5)最后调用即可。

运行程序,单击按钮,结果如下: 

结论:可以看到,对于.Net自身提供的类对象,我们对它直接强制转换即可。不会出现任何问题。

二、自定义对象

前面已经说过,对于自定义的对象,进行强制转换会抛出异常。因此,我们需要做些变通才行。

我们说,动态加载的Dll和手工添加的dll引用,系统会认为不是同一个Assembly。那么应该怎么解决?想一想,对了,应该使用接口。但是,这里使用接口的方法稍微有点特殊。还是先按步骤来讲解吧。 1、创建一个接口,该接口包括要加载对象类的方法、属性等:

新建一个“类库”项目,取名为AutoObjectInterface:

using System;

namespace AutoObjectInterface
{    
    public interface IAutoObject
    {
        void Print(string s);
     }        
}

这个接口很简单,只是提供一个Print()方法而已。

然后将它编译为Dll文件,名为AutoObjectInterface。

2、创建自定义类对象:

新建一个“类库”项目,取名为AutoObject,添加前面创建的接口Dll引用:

using System;

namespace AutoObject
{
    public class TestObject:AutoObjectInterface.IAutoObject
    {        
        public TestObject()
        {
            
         }

        public void Print(string s)
        {
             Console.WriteLine(s);
         }
     }
}

这个类实现了第一步创建的接口。注意,这里实现的接口不是直接写在该类中,而是独立的Dll。在这个类中,是添加了该接口的dll,然后再实现它。这就是前面说的使用接口的一点特殊性。为什么要这样,是因为后面动态加载时,也要引用该接口Dll。我们动态创建后的对象,正是转换为该接口对象。由于实际的类和动态创建的类都引用并实现了该接口Dll,因此它的转换才能成功。这正是实现的关键! 也许有人疑问:我们能否将接口就放在要创建的类中,然后实现它。编译成dll文件,然后动态加载该dll,同时也手动添加该dll。动态创建后的对象,再强制转换为这个接口类型,不可以吗?答案当然是否定的,为什么?别问我,我也不知道!总之,我现在讲的方法,才是通过反射动态创建自定义对象的不二法门!!

也许会有人说我太武断!如果你不信,去试试。如果用另外的方法能成功,我一定改正错误。至少现在我能这样武断。

言归正传。现在我们再将给类编译为dll。名为AutoObject.dll,放到e:/NewObject中。

3、利用反射动态创建该对象:

新建一个控制台项目,取名为StudyReflection,添加前面创建的接口Dll引用。代码如下:AutoObjectInterface.IAutoObject

Obj = (AutoObjectInterface.IAutoObject)obj;

using System;
using System.Reflection;


namespace studyReflection
{    
    class Class1
    {
        
        // 应用程序的主入口点。        
         [STAThread]
        static void Main(string[] args)
        {
             Assembly assembly = Assembly.LoadFrom(@"e:NewObjectAutoObject.dll");
             Type type = assembly.GetType("AutoObject.TestObject");

            object obj = Activator.CreateInstance(type);
             AutoObjectInterface.IAutoObject iObj = (AutoObjectInterface.IAutoObject)obj;

             iObj.Print("wayfarer");
            
             Console.ReadLine();
         }
     }
}

说明:这个代码和前面创建.Net自身提供的对象差不多,关键的区别就是强制转换。因为是自定义对象,所以我们不知道转换为什么对象啊,所以要添加接口的引用。转换的时候就转换为接口的类型:

这样我们就可以通过接口对象实例来调用类对象的方法Print()了。运行后,一切OK。

结论:在通过反射动态创建对象时,一定要注意区别所创建对象的类型。如果是自定义对象,必须通过单独的接口,来进行类型的转换。

C# 利用反射动态创建对象[摘录]的更多相关文章

  1. C# 利用反射动态创建对象——带参数的构造函数和String类型

    C# 利用反射动态创建对象——带参数的构造函数和String类型 最近笔者有一个想法需要利用反射动态创建对象(如string,int,float,bool,以及自定义类等)来实现,一直感觉反射用不好, ...

  2. C#利用反射动态创建对象 带参数的构造函数和String类型 (转载)

    最近笔者有一个想法需要利用反射动态创建对象(如string,int,float,bool,以及自定义类等)来实现,一直感觉反射用不好,特别是当构造函数带参数的时候.MSDN上给出的例子十分复杂,网上的 ...

  3. 【转】C# 利用反射动态创建对象

    http://www.cnblogs.com/Jan_Dai/archive/2010/11/09/1872812.html Activator.CreateInstance(Type.GetType ...

  4. C#回顾 - 8.利用反射动态创建对象

    拿微信消息返回的示例数据实验 var data = "<xml><ToUserName><![CDATA[toUser]]></ToUserName ...

  5. C# 利用反射动态加载dll

    笔者遇到的一个问题,dll文件在客户端可以加载成功,在web端引用程序报错.解决方法:利用反射动态加载dll 头部引用加: using System.Reflection; 主要代码: Assembl ...

  6. C# 知识点笔记:IEnumerable<>的使用,利用反射动态调用方法

    IEnumerable<T>的使用 创建一个IEnumerable对象 List<string> fruits = new List<string> { " ...

  7. .Net Core利用反射动态加载类库的方法(解决类库不包含Nuget依赖包的问题)

    在.Net Framework时代,生成类库只需将类库项目编译好,然后拷贝到其他项目,即可引用或动态加载,相对来说,比较简单.但到了.Net Core时代,动态加载第三方类库,则稍微麻烦一些. 一.类 ...

  8. protobuf在java应用中通过反射动态创建对象(DynamicMessage)

    ---恢复内容开始--- 最近编写一个游戏用到protobuf数据格式进行前后台传输,苦于protobuf接受客户端的数据时是需要数据类型的如xxx.parseForm(...),这样就要求服务器在接 ...

  9. StructureMap.dll 中的 GetInstance 重载 + 如何利用 反射动态创建泛型类

    public static T GetInstance<T>(ExplicitArguments args); // // Summary: // Creates a new instan ...

随机推荐

  1. 快速搭建sonar代码质量管理平台

    安装 下载,直接解压http://www.sonarqube.org/downloads/ 添加mysql驱动至\extensions\jdbc-driver\mysql\ 创建mysql数据库和用户 ...

  2. MVC Partial页面的使用

    先建立Action: public PartialViewResult CurrentCount() { ViewBag.Count = CurrentUserCount; return Partia ...

  3. HDU 5707 Combine String(动态规划)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5707 题意: 给你三个字符串 S1, S2, S3, 让你判断 S3 是否恰好由字符串 S1 和 S2 ...

  4. Windows 下安装 tensorflow & keras & opencv 的避坑指南!

    安装 Anaconda3 关键的一步: conda update pip 下面再去安装各种你需要的包,一般不会再报错. pip install -U tensorflow pip install -U ...

  5. python升级带来的yum异常:File "/usr/bin/yum", line 30

    问题: $ yum File "/usr/bin/yum", line 30 except KeyboardInterrupt, e: ^ SyntaxError: invalid ...

  6. Sea Battle<海战>(思路题)

    time limit per test1 second memory limit per test256 megabytes inputstandard input outputstandard ou ...

  7. redis:CLUSTER cluster is down 解决方法

    redis:CLUSTER cluster is down 解决方法 首先进入安装目录的src下: 1. ./redis-trib.rb check 127.0.0.1:7001 检查问题所在 2.  ...

  8. NOI2005 维护数列(splay)

    学了半天平衡树,选择了一道题来写一写,发现题目是裸的splay模板,但是还是写不好,这个的精髓之处在于在数列的某一个位置加入一个数列,类似于treap里面的merge,然后还学到了题解里面的的回收空间 ...

  9. [BZOJ4032][HEOI2015]最短不公共子串(Trie+DP)

    在虐各种最长公共子串.子序列的题虐的不耐烦了之后,你决定反其道而行之——被它们虐. 操作一:对A,B分别建SAM,暴力BFS. 操作二:对B建序列自动机或SAM,A在上面暴力匹配. 操作三:对A,B建 ...

  10. [BZOJ4016][FJOI2014]最短路径树问题(dijkstra+点分治)

    4016: [FJOI2014]最短路径树问题 Time Limit: 5 Sec  Memory Limit: 512 MBSubmit: 1796  Solved: 625[Submit][Sta ...