在接口中使用泛型时无法转换/匹配类型,为什么?

Zer*_*tor 5 c# generics types casting

在IMyMessage.cs中

public interface IMyMessage
{
}
Run Code Online (Sandbox Code Playgroud)

在IMyMessageReceiver.cs中

public interface IMyMessageReceiver<T> where T: IMyMessage
{
    void HandleMessage(T message);
    void Subscribe();
}
Run Code Online (Sandbox Code Playgroud)

在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
    }
}
Run Code Online (Sandbox Code Playgroud)

所以我希望使用一堆"IMyMessages"和"IMyMessageReceivers"来传递消息.我之前做了一个硬编码的方法但是厌倦了100个不同的发布/订阅函数名称,所以我想我会把它完美地包装在泛型中.

我的问题是,在使用泛型时我无法使代码工作.即使我指定Type T将是IMyMessage,我也不能在任何需要IMyMessage的地方使用T. 也许我只是习惯了基础/扩展类,因为它可以很好地工作.我已经尝试了从铸造到真正通用的各种方法,但我总是遇到同样的问题.

Pat*_*irk 4

好的,这就是我如何看待它的工作方式。由于您尝试以不受支持的方式使用协方差,因此您需要避免在某些地方使用泛型。但这样做不会失去任何类型安全性。

创建一个非泛型IMessageReceiver接口,以便不能使用泛型参数的类型可以使用它:

public interface IMyMessageReceiver
{
    void HandleMessage(IMyMessage message);

    void Subscribe();
}

public interface IMyMessageReceiver<in T> : IMyMessageReceiver
    where T : IMyMessage
{
    void HandleMessage(T message);
}
Run Code Online (Sandbox Code Playgroud)

如果您愿意,您可以创建一个基类来简化事情:

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();
}
Run Code Online (Sandbox Code Playgroud)

然后您可以更改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);
}
Run Code Online (Sandbox Code Playgroud)

这个类的具体内容是这样的:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后(最后),你的静态类将如下所示:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以像这样使用静态类:

MyMessagePublisher.Subscribe(new FooMessageReceiver());
MyMessagePublisher.Publish(new FooMessage());
Run Code Online (Sandbox Code Playgroud)