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

时间:2016-03-08 08:26:08

标签: c# generics casting

我有一个自定义消息传递系统,我试图转换它,因此它可以支持插件。

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

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

IConsumer<AMessage>

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

FileProcessor : IConsumer<FileMessage>

FileMessage : AMessage

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

现在,在核心中,它加载了插件并扫描了&amp;找到了我想将消费者链接到消息系统的消费者。

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

IConsumer<AMessage> consumer = IConsumer<AMessage> new FileProcessor();

我在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);
}

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

var channel = flow.From(FileProducer).To(FileConsumer);

使用flow.From:

public Channel<T> From<T>(IProducer<T> producer) where T : AMessage
{
    ...
}

和channel.To:

public class Channel<T> where T : AMessage 
{

   public Channel<T> To(IConsumer<T> consumer){
      ...
   }
}

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

- EDIT1 -

- EDIT2 -

需要进行向下转换是因为我正在尝试从Core系统中的JSON定义重建通道。

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

var consumers = new Dictionary<string, IProcessor>();

我不能在这里为消费者使用IConsumer,因为对所有消费者而言,T不一定相同。

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

- EDIT2 -

- 溶液 -

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

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

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

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

- 溶液 -

谢谢!

2 个答案:

答案 0 :(得分:2)

您需要使用out关键字将您的界面标记为covariant

IConsumer<out AMessage>

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

看到你的编辑,你不需要协方差而是逆变(IConsumer<in AMessage>)但是这个演员:

IConsumer<AMessage> consumer = (IConsumer<AMessage>)new FileProcessor();

无效。来自msdn:

  

逆变   使您可以使用比最初指定的更通用(更少派生)的类型。   您可以指定IEnumerable&lt; Base&gt;的实例到IEnumerable&lt; Derived&gt;类型的变量。

答案 1 :(得分:0)

如果在IConsumer<T>界面中T仅出现在返回值中,那么您可以使界面协变(在.NET 4及更高版本中):

public interface IConsumer<out T>

在这种情况下,此初始化将起作用:

IConsumer<AMessage> consumer = new FileProcessor();

但是如果方法参数中也使用了T,那么你的界面就不能协变。哪个好,请参阅IList<T>,例如:如果允许IList<Animal> cats = new List<Cat>(),则可以调用cats.Add(new Dog()),这是不可取的。 :)