MEF - ImportMany具有通用接口&amp;懒惰<t,tmetadata =“”> </t,>

时间:2012-06-19 17:45:32

标签: c# generics mef

我正在使用MEF(特别是MEF 2 Preview 5)开发一个应用程序,并且我在尝试基于通用接口导入时遇到了问题。

我有一个界面:

public interface IMessageHandler<in T>
{
    void HandleMessage(T message);
}

其中T是要处理的消息类型。我正在使用RegistrationBuilder

将这些内容导入目录
RegistrationBuilder context = new RegistrationBuilder();

context.ForTypesDerivedFrom(typeof(IMessageHandler<>))
    .Export(builder => builder.AsContractType(typeof(IMessageHandler<>)));

然后,在消费类中,我使用[ImportMany]将这些列表导入IEnumerable<Lazy>>

[ImportMany(typeof(IMessageHandler<>))]
IEnumerable<Lazy<IMessageHandler<object>, HandledMessageTypeAttribute>> _messageHandlers;

现在,这是第一个问题 - 此时你被迫为通用接口分配一个类型。我正在使用Lazy<T, TMetadata>,因为IMessageHandler<T>实现具有我想要使用的相关元数据(HandledMessageTypeAttribute)。

现在,当我想访问IEnumerable<Lazy<>>集合中的任何元素时,我得到以下异常:

Cannot cast the underlying exported value of type 
'MessageHandlerImplementation (ContractName="IMessageHandler(System.Object)")' 
to type 'IMessageHandler`1[System.Object]'.

我理解(粗略地)为什么我得到了例外,问题是我不知道如何绕过它。所以,基本上我想做的是:

  1. 有一堆实现IMessageHandler<T>接口的类。
  2. 在运行时使用MEF发现它们。
  3. 将它们导入一个集合,允许我使用他们拥有的任何元数据。
  4. 能够实例化它们。
  5. 我知道我可以简单地使IMessageHandler非通用,让IMessageHandler.HandleMessage()接受object类型的参数,但我正在寻找更优雅的解决方案

    赞赏任何指示或指导。

1 个答案:

答案 0 :(得分:3)

在没有使用非通用接口的情况下,我没有看到更好的方法来实现您想要实现的目标。问题的根源是接口的定义:

public interface IMessageHandler<in T>

这意味着如果我们有两个类AB,其中B来自A,那么这是允许的

IMessageHandler<B> handler = new AHandler();

但这不是:

IMessageHandler<A> handler = new BHandler();

你实际上是在尝试执行后者,这是引发异常的原因。我假设您想要做的是能够获得一个类型的处理程序。如果是这种情况,那么您应该使用非通用接口并在导出元数据中使用消息类型。然后你会有这样的事情:

public IMessageHandler GetHandler<T>()
{
    Type handlerType = typeof(T);
    return _messageHandlers.FirstOrDefault(x => x.Metadata.MessageType == handlerType);
}

您也可能会发现this question相关。希望这会有所帮助。