2013-12-18 83 views
5

在IMyMessage.cs在接口中使用泛型時無法投射/匹配類型,爲什麼?

public interface IMyMessage 
{ 
} 

在IMyMessageReceiver.cs

public interface IMyMessageReceiver<T> where T: IMyMessage 
{ 
    void HandleMessage(T message); 
    void Subscribe(); 
} 

在MyMessagePublisher.cs

public static class MyMessagePublisher 
{ 
    private static Dictionary<Type, List<IMyMessageReceiver<IMyMessage>>> _subscribers; 

    static MyMessagePublisher 
    { 
     _subscribers = new Dictionary<Type, List<IMyMessageReceiver<IMyMessage>>>(); 
    } 

    public static function Subscribe<T>(IMyMessageReceiver<T> receiver) where T: IMyMessage 
    { 
     Type messageType = typeof (T); 
     List<IMyMessageReceiver<IMyMessage>> listeners; 

     if(!_subscribers.TryGetValue(messageType, out listeners)) 
     { 
      // no list found, so create it 
      List<IMyMessageReceiver<T>> newListeners = new List<IMyMessageReceiver<T>>(); 
      // ERROR HERE: Can't convert List<IMyMessageReceiver<T>> to List<IMyMessageReceiver<IMyMessage>> 
      _subscribers.add(messageType, newListeners); 

     } 

     // I would then find the right list and add the receiver it to it but haven't got this far 
    } 
} 

所以我希望是用一堆 'IMyMessages' 和「IMyMessageReceivers的'傳遞消息。我早些時候做了一個硬編碼的方法,但厭倦了100個不同的發佈/訂閱函數名稱,所以我想我會把它很好地包裝到泛型中。

我的問題是,我不能讓代碼在使用泛型時工作。即使我指定類型T將是IMyMessage,我不能在任何需要IMyMessage的地方使用T.也許我只是習慣於基礎/擴展類,因爲它可以在那裏很好地工作。我嘗試過從鑄造到真正通用的各種方法,但我總是遇到同樣的問題。

+1

你試圖在協變的方式使用'T',但在C#的輸入參數,只能進行逆變 – Sean

+0

@Sean,是這裏有一個解決方案那麼還是應該嘗試創建一個MyMessage/MyMessageReceiver類並擴展它們呢?我不太喜歡這種聲音,因爲它增加了一些困難。 – Zeritor

+0

@Zeritor - 我在下面添加了一個答案。 – Sean

回答

4

確定這裏是我如何看到它的工作。由於您試圖以不受支持的方式使用協變,因此您需要避免在少數地方使用泛型。但這樣做不會失去任何類型安全。

創建非通用IMessageReceiver接口,使得無法使用泛型參數類型可以用這個來代替:

public interface IMyMessageReceiver 
{ 
    void HandleMessage(IMyMessage message); 

    void Subscribe(); 
} 

public interface IMyMessageReceiver<in T> : IMyMessageReceiver 
    where T : IMyMessage 
{ 
    void HandleMessage(T message); 
} 

您可以創建一個基類,以簡化的東西,如果你想:

public abstract class MyMessageReceiverBase<T> : IMyMessageReceiver<T> 
    where T : IMyMessage 
{ 
    public abstract void HandleMessage(T message); 

    public void HandleMessage(IMyMessage message) 
    { 
     if (!(message is T)) 
      throw new InvalidOperationException(); 
     HandleMessage((T)message); 
    } 

    public abstract void Subscribe(); 
} 

然後你可以改變IMyMessageListeners使用非通用版本,因爲它並不真正需要的泛型類型反正:

public interface IMyMessageListeners 
{ 
    void Add(IMyMessageReceiver receiver); 

    // I added this since I think this is how you're going to use it 
    void Send(IMyMessage message); 
} 

這個類的具體是這樣的:

public class MyMessageListeners : IMyMessageListeners 
{ 
    readonly List<IMyMessageReceiver> _list = new List<IMyMessageReceiver>(); 

    public void Add(IMyMessageReceiver receiver) 
    { 
     _list.Add(receiver); 
    } 

    public void Send(IMyMessage message) 
    { 
     foreach (var listener in _list) 
      listener.HandleMessage(message); 
    } 
} 

然後(最終),您的靜態類看起來就像這樣:

public static class MyMessagePublisher 
{ 
    static readonly Dictionary<Type, IMyMessageListeners> _subscribers = new Dictionary<Type, IMyMessageListeners>(); 

    // I added this too, since I think this is how you intend to use it 
    public static void Publish<T>(T message) where T : IMyMessage 
    { 
     Type messageType = typeof(T); 
     IMyMessageListeners listeners; 

     if (_subscribers.TryGetValue(messageType, out listeners)) 
      listeners.Send(message); 
    } 

    public static void Subscribe<T>(IMyMessageReceiver<T> receiver) where T : IMyMessage 
    { 
     Type messageType = typeof(T); 
     IMyMessageListeners listeners; 

     if (!_subscribers.TryGetValue(messageType, out listeners)) 
     { 
      // no list found, so create it 
      listeners = new MyMessageListeners(); 
      _subscribers.Add(messageType, listeners); 
     } 

     listeners.Add(receiver); 
    } 
} 

而且你可以用你的靜態類,如下所示:

MyMessagePublisher.Subscribe(new FooMessageReceiver()); 
MyMessagePublisher.Publish(new FooMessage()); 
+0

您的解決方案與使用List >無論如何都可以工作。如果使用IMyMessageListeners insy IMyMessageListeners 它會工作嗎? –

+0

Gah,你說得對,它是完全相同的,'Add(receiver)'調用無論如何都不起作用。我會重新考慮這一點,但如果我不能拿出任何東西,可能會撤回我的答案。 –

+0

''定義中有什麼進/出做什麼?如果你不認爲這有效,我會暫緩試用。我覺得我正在解決這個問題。 – Zeritor

0

泛型不支持協方差。因此,即使T實現IMyMessage,IMyMessageReceiver T也不能被轉換爲IMyMessageReceiver IMyMessage。這就是爲什麼你得到你的錯誤。

我不認爲你應該在IMyMessageReceiver接口中使用泛型。我不知道你想達到什麼,但也許這樣的事情會做的伎倆:

public interface IMyMessageReceiver 
{ 
    void HandleMessage(IMyMessage message); 
    void Subscribe(); 
} 

public class MyMessageReceiver<T> : IMyMessageReceiver where T: IMyMessage 
{ 
    void IMyMessageReceiver.HandleMessage(IMyMessage message) 
    { 
     HandleMessage(message as T); 
    } 

    public void HandleMessage(T message) {...} 
    public void Subscribe() {...} 
} 
0

既然你知道的類型將永遠是IMyMessageReceiver<T>你有仿效storin行爲摹寄託都作爲對象和鑄造:

private static Dictionary<Type, List<object>> _subscribers; 


    public static function Subscribe<T>(IMyMessageReceiver<T> receiver) where T: IMyMessage 
    { 
     Type messageType = typeof (T); 
     List<object> listeners; 

     if(!_subscribers.TryGetValue(messageType, out listeners)) 
     { 
     // no list found, so create it 
     List<object> newListeners = new List<object>(); 
     newListeners.Add(receiver) 
     _subscribers.add(messageType, newListeners); 

     } 

     var messageReceivers = listeners.Cast<IMyMessageReceiver<T>>(); 
} 

既然你知道了字典中的列表將永遠是一個給定類型的,你可以放心地施展他們!

+0

泛型的要點是避免使用動態投射的醜陋對象容器! –

+0

@AdrienBuet - 是的,但有時你必須賣掉你的靈魂! – Sean

0

在這種情況下KeyedByTypeCollection<T>是合適的:

public interface IMyMessage 
{ 
} 

public interface IMyMessageReceiver<T> where T : IMyMessage 
{ 
    void HandleMessage(T message); 
    void Subscribe(); 
} 

public static class MyMessagePublisher 
{ 
    private static readonly KeyedByTypeCollection<IList> Subscribers; 

    static MyMessagePublisher() 
    { 
     Subscribers = new KeyedByTypeCollection<IList>(); 
    } 

    public static void Subscribe<T>(IMyMessageReceiver<T> receiver) where T : IMyMessage 
    { 
     List<IMyMessageReceiver<T>> listeners = Subscribers.Find<List<IMyMessageReceiver<T>>>(); 

     if (listeners == null) 
     { 
      listeners = new List<IMyMessageReceiver<T>>(); 
      Subscribers.Add(listeners); 
     } 

     // Now you can use the listeners list 

     listeners.Add(receiver); 
    } 
} 
相關問題