通过反射动态实例化对象中出现的一个奇怪问题

在.Net中通过反射,可以对程序集进行很多操作。现在我希望通过动态加载程序集,并将该程序集中的一个类对象进行实例化。然后将获得这个object对象通过强制转换,转换为具体的类对象,以达到调用其方法的目的。

我要加载的程序集很简单,只有一个类,且类里面只有一个公有属性和一个公共方法。代码如下:

using System;

namespace AutoObject
{
  
public class TestObject
    
{
        
public string name;

        
public TestObject()
        
{
            name = "wayfarer";
        }


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


    }

}

程序集名为AutoObject.dll,路径假设为”e:\”。命名空间为AutoObject,类名为TestObject。

然后我编写一个控制台应用程序来动态加载它:

using System;
using System.Reflection;

namespace UseReflector
{    
    
class Class1
    
{
        
        [STAThread]
        
static void Main(string[] args)
        
{
            
object obj = CreateObject(@"e:\AutoObject.dll");
            AutoObject.TestObject test 
= (AutoObject.TestObject)obj;
            test.Print(
"I love you!");
            Console.ReadLine();
        }


        
public static object CreateObject(string assemblyFile)
        
{
            Assembly assembly 
= Assembly.LoadFrom(assemblyFile);
            Type type 
= assembly.GetType("AutoObject.TestObject");
            
            
object obj = Activator.CreateInstance(type);
            
return obj;
        }

    }

}

主方法是CreateObject()。

首先通过Assembly.LoadFrom()加载该程序集,然后通过GetType()方法获得指定类名的对象类型。此时type的name为AutoObject.TestObject。

然后通过Activator.CreateInsatance(type),来调用构造函数获得该类对象的实例,返回object类型对象。

然后再Main()中,将上述方法创建的对象强制转换为AutoObject.TestObject类型。当然,我在控制台程序中又手工添加了对AutoObject.dll的引用。最后调用转换后的对象test的Print()方法。

运行,结果抛出异常“指定的转换无效。”

我用断点调试,在执行CreateObject()方法的最后一句:return obj时,得到obj的信息是:

obj的值是{AutoObject.TestObject},类型为System.Object。同时还看到类对象的属性name,其值为“wayfarer“,类型为string。显然它是和TestObject类对象符合的啊,为什么不能转换呢?

我在Main()中引入了另一个实例:
object obj2 = new AutoObject.TestObject();
再将前面创建好的obj赋给obj2,再对obj2进行如前的转换:
AutoObject.TestObject test = (AutoObject.TestObject)obj2;

结果报告的错误是一样的。但我经过断点调试,发现obj和obj2两个对象的调试信息都和上面一样,可为什么结果同样是错误的呢?

但如果我把前面获得的type改为直接从类对象获得,运行就正常了。

原来的:Type type = assembly.GetType("AutoObject.TestObject");

修改后: Type type = typeof(AutoObject.TestObject);
 
这又是为什么?事实上两种方法获得的type都是AutoObject.TestObject类型啊?

真实百思不得其解!

posted on 2004-07-20 20:50 张逸 阅读(3539) 评论(15)  编辑 收藏 所属分类: .NET FrameWork

评论

#1楼  2004-07-20 21:57 spool [未注册用户]

既然是通过反射来加载程序集,那么就不能这么用:
AutoObject.TestObject test = (AutoObject.TestObject)obj;
            test.Print("I love you!");

要用类似invoke的方法(赫赫,具体怎么写现在没有编译环境,忘了语法了,你自己查一下吧)   回复  引用    

#2楼  2004-07-20 21:59 鞠强      

对于test.Print方法,也要用invok来做。具体方式,和你实例化某个class是一样的,呵呵。自己想想吧!!!

代码是very very easy的。。。   回复  引用  查看    

#3楼  2004-07-20 22:11 wayfarer      

Invoke()方法我是明白的。通过GetMethods()方法获得MethodInfo类对象,然后再通过Invoke()来调用。

Type type = assembly.GetType("类型名");
MethodInfo[] mi = type.GetMethods(flags);
Object obj = Activator.CreateInstance(type);
foreach(MethodInfo m in mi)
{
m.Invoke(obj, null);
}

不过我的开发目标是设计一个组件,通过反射动态加载程序集后,获得主类的类对象实例。这个组件只是创建该对象而已。至于该对象方法的调用是应用程序的事,与组件无关。

难道不能通过反射来创建对象以获得和new相似的结果吗?

  回复  引用  查看    

#4楼  2004-07-20 22:13 Ying-Shen      

根据我的理解这个问题和.NET CLR Loader 的Load Context有关
简而言之,通过LoadFrom加载的类型和通过Load或early binding加载的类型是不等价的。(因为属于两个不同的context)。
具体的内容您可以参考:

http://blogs.msdn.com/suzcook/archive/2003/05/29/57143.aspx
还有我的blog:
http://www.cnblogs.com/jonnyyu/archive/2004/06/23/18159.aspx
http://www.cnblogs.com/jonnyyu/archive/2004/06/23/18160.aspx
  回复  引用  查看    

#5楼  2004-07-21 03:02 hBifTs      

嗯,像这种问题是由于在不同的Assembly中,你自己觉得是一样的类,在JIT来看,是不一样的.
因为他们有不一样的标识符..
你这样强制转换肯定会出错的.   回复  引用  查看    

#6楼  2004-07-21 09:28 wayfarer      

hBifTs分析的很有道理。

那看来我想实现的目标无法达到了。   回复  引用  查看    

#7楼  2004-07-21 09:45 wayfarer      

hBifTS“像这种问题是由于在不同的Assembly中,你自己觉得是一样的类,在JIT来看,是不一样的. ”。

不过我在实现的时候,其实动态加载和人工添加程序集引用的是同一个Assembly啊。莫非在framework中,通过反射加载的程序集和人工添加,最后连类型对象都有所不同?

  回复  引用  查看    

#8楼  2004-07-22 21:54 Ying-Shen      

to Wayfarer,
关键是你的反射的assembly是通过LoadFrom加载的
如果是Load就没问题。   回复  引用  查看    

#9楼  2004-07-22 21:58 Ying-Shen      

你如果想要更深入的回答可以去博客堂问 junfeng zhang,
他应该比较清楚。   回复  引用  查看    

#10楼  2004-07-22 22:03 wayfarer      

to Ying-Shen:
我不认为是LoadFrom的问题。通过反射创建实例的问题,我已经解决。有兴趣可以看文章《利用反射动态创建对象》。当然我更期待更加完美的解决方案。

顺便问一下,Load()的参数是什么?因为MSDN上的方法签名中,参数是程序集名,为string类型。那么是否包括程序集的路径?可以举个使用Load()的例子呢?

  回复  引用  查看    

#11楼  2004-07-22 22:04 wayfarer      

好的。我可以去问问。谢谢!   回复  引用  查看    

#12楼  2004-07-22 22:16 wayfarer      

看了junfeng zhang的博客,知道如果要用Load()来加载程序集,这个程序集必须放到GAC中。

我可以试试,不过还是不报希望啊:)   回复  引用  查看    

#13楼  2004-07-23 23:00 wayfarer      

经过Ying-Shen的提醒,在MSN又请教了junfeng zhang。看了Suzanne的博客。基本上弄明白了问题之所在。

基本上说,正如hBifTS所说,动态加载和静态引用的程序集并不是同一个Assembly了。事实上,在.Net中,同一个应用程序域并不允许同时加载两个相同的Assembly。即使加载了,也会认为是两个不同的程序集。如果要同时加载两个,则必须在不同的应用程序域中。可以通过AppDomain创建一个新的应用程序域,在其中动态加载;而原来的程序域则静态添加引用。此时将会认为是同一个程序集。

猜测是如此。我需要测试。想到我最近作的Remoting。服务器端和客户端正是两个不同的应用程序域。于是我在服务器端通过Activator.CreateInstance()动态创建对象,返回object类型。
然后再客户端静态添加该对象的引用。(我在本地机上试验,所以服务器端和客户端加载的程序集完全一样,包括路径都相同)然后再客户端通过Activator.GetObject()获得服务器端动态创建的对象,再显示进行强制转换。果然,使正确的。   回复  引用  查看    

#14楼  2004-10-22 09:08 ww [未注册用户]

我怎么可以   回复  引用    

#15楼  2007-07-14 22:50 Michael Wang [未注册用户]

换成Assembly assembly = Assembly.LoadFile(assemblyFile);就可以了吧
  回复  引用    


标题  
姓名  
主页
Email (博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2004-07-21 09:45 编辑过


相关链接:
 



导航

公告

logo.gif
我的著作与译作

《软件设计精要与模式》

《WCF服务编程》

MVP_Horizontal_BlueOnly.png

From 03-03-2006
Counter: site stats

与我联系

搜索

 

常用链接

我参加的小组

我参与的团队

随笔分类(245)

随笔档案(237)

最新随笔

积分与排名

最新评论

阅读排行榜

评论排行榜