在接口中使用泛型时无法强制转换/匹配类型,原因

本文关键字:转换 原因 类型 接口 泛型 | 更新日期: 2024-10-26 05:05:17

In 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。也许我只是习惯于基础/扩展类,因为它在那里可以正常工作。我尝试了从铸造到真正通用的各种方法,但我总是遇到同样的问题。

在接口中使用泛型时无法强制转换/匹配类型,原因

好的,

这就是我可以看到它的工作原理。 由于您尝试以不受支持的方式使用协方差,因此您需要避免在几个位置使用泛型。 但这样做不会失去任何类型安全性。

创建一个非泛型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());

型不支持协方差。因此,即使 T 实现了 IMyMessage,IMyMessageReceiver 也无法转换为 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() {...}
}

鉴于您知道类型将始终IMyMessageReceiver<T>您通过将 everthing 存储为对象并强制转换来模拟行为:

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>>();
}

由于您知道字典中的列表将始终是给定的类型,因此您可以放心地投射它们!

在这种情况下,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);
    }
}