2013-12-18 50 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); 
    } 
} 
相关问题