System.InvalidCastException:无法将类型为x的对象强制转换为y

mve*_*and 5 c# generics casting

我有一个自定义消息系统,我试图转换,所以它可以支持插件.

该系统有生产者和消费者.两者都必须指定它们生成或使用的消息类型.

对于消费者来说,要实现的接口是

IConsumer<AMessage>
Run Code Online (Sandbox Code Playgroud)

我有一个插件dll中的消费者,它实现了这个接口:

FileProcessor : IConsumer<FileMessage>
Run Code Online (Sandbox Code Playgroud)

FileMessage : AMessage
Run Code Online (Sandbox Code Playgroud)

而AMessage是一个抽象类.IConsumer和AMessage都在主(Core)程序集中.

现在,在核心中,它加载了插件并扫描并找到了我希望将消费者链接到消息系统的消费者.

为了简化这里的事情,我只是尝试将消费者放在一个变量中:

IConsumer<AMessage> consumer = IConsumer<AMessage> new FileProcessor();
Run Code Online (Sandbox Code Playgroud)

我在VS中收到警告:

可疑演员:解决方案中没有类型继承自'TestPlugin.FileProcessor'和'Core.IConsumer'.

当我执行时,我得到了

System.InvalidCastException:无法将类型为'TestPlugin.FileProcessor'的对象强制转换为'Core.IConsumer`1 [Core.AMessage]'.

为什么这个演员会失败?我怎么解决这个问题?

--edit1--

根据评论,这里是IProducer和IConsumer的完整定义:

public interface IProducer<out T> : IProcessor where T : AMessage
{
    event MessageSender<T> SendMessage;
}

public interface IConsumer<in T> : IProcessor where T : AMessage
{
    ProcessResult ProcessBean(T bean);
}
Run Code Online (Sandbox Code Playgroud)

当生产者在系统中链接时,他们定义T,之后只有具有相同类型T的消费者可以链接到该生产者.例如:

var channel = flow.From(FileProducer).To(FileConsumer);
Run Code Online (Sandbox Code Playgroud)

随着flow.From:

public Channel<T> From<T>(IProducer<T> producer) where T : AMessage
{
    ...
}
Run Code Online (Sandbox Code Playgroud)

和频道.要:

public class Channel<T> where T : AMessage 
{

   public Channel<T> To(IConsumer<T> consumer){
      ...
   }
}
Run Code Online (Sandbox Code Playgroud)

因此,生产者为消费者定义了T的类型.

--edit1--

--edit2--

向下转换的需要是因为我试图从Core系统中的JSON定义重建通道.

因此,消费者(和生产者)是从插件程序集动态构造的,并添加到临时字典中(当检测到并实例化时):

var consumers = new Dictionary<string, IProcessor>();
Run Code Online (Sandbox Code Playgroud)

我不能在这里为消费者使用IConsumer,因为对于所有消费者来说T不一定相同.

然后在构建频道时,我将通过它的id(字典的键)查找消费者,并将其提供给频道的To(...)方法.这失败是因为字典保存了IProcessor,动态构造的通道具有"To"签名 public Channel<T> To(IConsumer<T> consumer).

--edit2--

- 解 -

感谢Luaan(见评论)我达到了以下解决方案:

在扫描插件程序集时,我最初将检测到的生产者和消费者存储在字典中<string, IProcessor>,现在我改变了这个<string, dynamic>解决了这个铸造问题的字典!

感谢所有为此解决方案添加的人!

@Luaan,我希望我能选择你的答案作为解决方案,但它在评论中......

- 解 -

谢谢!

Ale*_*rck 2

您需要使用关键字将您的接口标记为协变out

IConsumer<out AMessage>
Run Code Online (Sandbox Code Playgroud)

这意味着您可以传递比指定类型更派生的类型,因此您可以使用FileMessage派生自 的a AMessage

看到您的编辑,您不需要协变,而是逆变 ( IConsumer<in AMessage>),但接下来是这样的转换:

IConsumer<AMessage> consumer = (IConsumer<AMessage>)new FileProcessor();
Run Code Online (Sandbox Code Playgroud)

无效。来自msdn:

逆变 使您能够使用比最初指定的更通用(派生较少)的类型。 您可以将 IEnumerable<Base> 的实例分配给 IEnumerable<Derived> 类型的变量。