2012-06-19 26 views
2

我正在研究一個使用MEF的應用程序(特別是MEF 2 Preview 5),並且我遇到了一個試圖基於通用接口導入的問題。MEF - ImportMany with generic interfaces&Lazy <T,TMetadata>

我有一個接口:

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. 能夠實例化它們。

我明白我可以簡單地做IMessageHandler非通用的,具有IMessageHandler.HandleMessage()接受object類型的參數,但我一直在尋找一個稍微更優雅的解決方案

任何指針或指導表示讚賞。

回答

3

我沒有看到更好的方式來做你想要實現的,而不使用非通用接口。問題的根源在於接口的定義:

public interface IMessageHandler<in T> 

這意味着,如果我們有兩個班,AB,其中BA派生,那麼這是允許

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相關的爲好。希望這可以幫助。