C# 如何正确投射通过反射创建的对象

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/31567/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 09:09:09  来源:igfitidea点击:

How to properly cast objects created through reflection

提问by Ben Robbins

I'm trying to wrap my head around reflection, so I decided to add plugin capability to a program that I'm writing. The only way to understand a concept is to get your fingers dirty and write the code, so I went the route of creating a simple interface library consisting of the IPlugin and IHost interfaces, a plugin implementation library of classes that implement IPlugin, and a simple console project that instantiates the IHost implementation class that does simple work with the plugin objects.

我试图围绕反射进行思考,所以我决定为我正在编写的程序添加插件功能。理解一个概念的唯一方法是让你的手指变脏并编写代码,所以我走的路线是创建一个由 IPlugin 和 IHost 接口组成的简单接口库,一个实现 IPlugin 的类的插件实现库,以及一个简单的控制台项目,它实例化 IHost 实现类,该类对插件对象进行简单的工作。

Using reflection, I wanted to iterate through the types contained inside my plugin implementation dll and create instances of types. I was able to sucessfully instantiate classes with this code, but I could not cast the created object to the interface.

使用反射,我想遍历包含在我的插件实现 dll 中的类型并创建类型的实例。我能够使用此代码成功实例化类,但无法将创建的对象转换为接口。

I tried this code but I couldn't cast object o as I expected. I stepped through the process with the debugger and the proper constructor was called. Quickwatching object o showed me that it had the fields and properties that I expected to see in the implementation class.

我尝试了这段代码,但无法按预期投射 object o。我用调试器逐步完成了这个过程,并调用了正确的构造函数。Quickwatching 对象 o 向我展示了它具有我希望在实现类中看到的字段和属性。

loop through assemblies
  loop through types in assembly
    // Filter out unwanted types
    if (!type.IsClass || type.IsNotPublic || type.IsAbstract )
      continue;
    // This successfully created the right object
    object o = Activator.CreateInstance(type);
    // This threw an Invalid Cast Exception or returned null for an "as" cast
    // even though the object implemented IPlugin      
    IPlugin i = (IPlugin) o;

I made the code work with this.

我使代码与此一起工作。

using System.Runtime.Remoting;
ObjectHandle oh = Activator.CreateInstance(assembly.FullName, type.FullName);
// This worked as I intended
IPlugin i = (IPlugin) oh.Unwrap();
i.DoStuff();

Here are my questions:

以下是我的问题:

  1. Activator.CreateInstance(Type t) returns an object, but I couldn't cast the object to an interface that the object implemented. Why?
  2. Should I have been using a different overload of CreateInstance()?
  3. What are the reflection related tips and tricks?
  4. Is there some crucial part of reflection that I'm just not getting?
  1. Activator.CreateInstance(Type t) 返回一个对象,但我无法将该对象强制转换为该对象实现的接口。为什么?
  2. 我应该使用不同的 CreateInstance() 重载吗?
  3. 有哪些与反射相关的提示和技巧?
  4. 是否有一些我没有得到的反思的关键部分?

采纳答案by lubos hasko

I'm just guessing here because from your code it's not obvious where do you have definition of IPlugin interface but if you can't cast in your host application then you are probably having IPlugin interface in your host assembly and then at the same time in your plugin assembly. This won't work.

我只是在这里猜测,因为从您的代码中,您在哪里定义 IPlugin 接口并不明显,但是如果您无法在主机应用程序中进行转换,那么您的主机程序集中可能有 IPlugin 接口,然后同时在你的插件程序集。这行不通。

The easiest thing is to make this work is to have IPlugin interface marked as public in your host assembly and then have your Plugin assembly reference host application assembly, so both assemblies have access to the very same interface.

最简单的方法是在主机程序集中将 IPlugin 接口标记为 public,然后让您的插件程序集引用主机应用程序程序集,这样两个程序集都可以访问相同的接口

回答by Haacked

Is your type not public, if so, call the overload that takes in a boolean:

您的类型是否不公开,如果是,请调用包含布尔值的重载:

Activator.CreateInstance(type, true);

Also, in your first example, see if o is null and if not, print out o.GetType().Name to see what it really is.

此外,在您的第一个示例中,查看 o 是否为空,如果不是,则打印出 o.GetType().Name 以查看它到底是什么。

回答by Ben Robbins

@Haacked

@被

I tried to keep the pseudocode simple. foreach's take up a lot of space and braces. I clarified it.

我试图保持伪代码简单。foreach 占用大量空间和大括号。我澄清了。

o.GetType().FullName returns Plugins.Multiply which is the expected object. Plugins.Multiply implements IPlugin. I stepped through the process in the debugger quite a few times until I gave up for the evening. Couldn't figure out why I couldn't cast it because I watched the constructor fire until I got grumpy about the whole mess. Came back to it this evening and made it work, but I still don't understand why the cast failed in the first code block. The second code block works, but it feels off to me.

o.GetType().FullName 返回 Plugins.Multiply 这是预期的对象。Plugins.Multiply 实现了 IPlugin。我在调试器中多次执行该过程,直到晚上我放弃了。无法弄清楚为什么我不能施展它,因为我看着构造函数着火,直到我对整个混乱感到脾气暴躁。今晚回到它并使其工作,但我仍然不明白为什么在第一个代码块中演员表失败。第二个代码块有效,但对我来说感觉不对。

回答by Ben Robbins

@lubos hasko

@卢博斯哈斯科

You nailed it on the nose. My original design did have three different assemblies with both the host and plugin implementation referencing the plugin interface assembly.

你把它钉在鼻子上。我的原始设计确实有三个不同的程序集,其中主机和插件实现都引用了插件接口程序集。

I tried a separate solution with a host implementation and interface assembly and a plugin implementation assembly. Within that solution, the code in the first block worked as expected.

我尝试了一个单独的解决方案,其中包含一个主机实现和接口程序集以及一个插件实现程序集。在该解决方案中,第一个块中的代码按预期工作。

You've given me a bit more to think about, because I'm not quite understanding why two assemblies referencing a common assembly don't get the same type out of the common assembly.

你给了我更多的思考,因为我不太明白为什么引用一个公共程序集的两个程序集不能从公共程序集中得到相同的类型。

回答by Ben Robbins

I was just trying to work this out myself and managed to stumble on the answer!

我只是想自己解决这个问题,并设法偶然发现了答案!

I had 3 different C# projects

我有 3 个不同的 C# 项目

  • A - Plugin Interface project
  • B - Host exe project -> references A
  • C - Plugin Implementation project -> references A
  • A - 插件接口项目
  • B - 主机 exe 项目 -> 引用 A
  • C - 插件实现项目 -> 参考 A

I was getting the casting error as well until I changed the assembly name for my Plugin Interface proj to match the namespace of what I was trying to cast to.

我也遇到了转换错误,直到我更改了插件接口项目的程序集名称以匹配我尝试转换的名称空间。

E.g.

例如

IPluginModule pluginModule = (IPluginModule)Activator.CreateInstance(curType);

was failing because the assembly that the IPluginModule interface was defined in was called 'Common', The -type- I was casting to was 'Blah.Plugins.Common.IPluginModule' however.

失败是因为定义 IPluginModule 接口的程序集被称为“Common”,但是我正在转换的类型是“Blah.Plugins.Common.IPluginModule”。

I changed the Assembly name for the interface proj to be 'Blah.Plugins.Common' meant that the cast then succeeded.

我将接口项目的程序集名称更改为“Blah.Plugins.Common”,这意味着转换成功了。

Hopefully this explanation helps someone. Back to the code..

希望这个解释对某人有所帮助。回到代码..

回答by Ben Robbins

hmmm... If you are using Assembly.LoadFrom to load your assembly try changing it Assembly.LoadFile instead.

嗯...如果您使用 Assembly.LoadFrom 加载程序集,请尝试更改它的 Assembly.LoadFile。

Worked for me

为我工作

From here: http://www.eggheadcafe.com/community/aspnet/2/10036776/solution-found.aspx

从这里:http: //www.eggheadcafe.com/community/aspnet/2/10036776/solution-found.aspx

回答by Robel Robel Lingstuyl

The link to egghead above is the main solution to the problem use Assembly.LoadFile() instead of .LoadFrom()

上面egghead 的链接是使用 Assembly.LoadFile() 而不是 .LoadFrom() 问题的主要解决方案