首页 > 解决方案 > 多个受约束的泛型类型的字典和接口

问题描述

我正在将 Java 项目转换为 C#,但遇到以下问题:

消息处理程序

 public interface IMessageHandler<T, H> where T : IPeerAttachment where H : IMessage {
    void HandleMessage(T clientAttachment, H message);
}

登录请求处理程序

public class LoginRequestHandler : IMessageHandler<LoginPeerAttachment , LoginRequest> {
    public void HandleMessage(LoginPeerAttachment clientAttachment, LoginRequest message) {

    }
}

消息处理程序注册表

 private readonly Dictionary<MessageId, IMessageHandler<IPeerAttachment, IMessage>> _handlers = new Dictionary<MessageId, IMessageHandler<IPeerAttachment, IMessage>>();

我想做的是:

LoginRequestHandler loginRequestHandler = new LoginRequestHandler();
_handlers[messageId] = loginRequestHandler;

这给了我一个编译错误,告诉我它需要是 IMessageHandler 类型。

我不确定为什么这不起作用,因为 LoginRequestHandler 仅实现接口中指定的派生类型。

任何建议如何解决这个问题?

标签: c#dictionarygenericsinterface

解决方案


好的,这是不允许的,因为它不是类型安全的。嗯?以下内容怎么可能不是类型安全的:

var _handlers = new Dictionary<MessageId, IMessageHandler<IPeerAttachment, IMessage>>();
var loginRequestHandler = new LoginRequestHandler();
_handlers[messageId] = loginRequestHandler;

LoginRequestHandler所以这里有IMessageHandler<IPeerAttachment, IMessage>什么问题吗?

好吧,假设以前是合法的,然后更进一步,看看会发生什么:

var handler = _handlers[meesageId];
handler.HandleMessage(logOutPeerAttachment, logOutMessage);

那合法吗?嗯,它确实看起来像。handler是类型的IMessageHandler<IPeerAttachment, IMessage>,因此HandleMessage可以处理提供的参数类型...

所以,现在我们遇到了可怕的情况;遵循完全合法的步骤,我们刚刚破坏了类型系统,因为我们以某种方式要求 aLoginRequestHandler处理 a LogOutRequest

显然,正如您亲身体验过的痛苦一样,并非所有步骤都是合法的。这种引用转换实际上是非法的:(IMessageHandler<IPeerAttachment, IMessage>)loginRequestHandler

为了使这种转换起作用, 的类型变化IMessageHandler需要是协变的,这意味着,泛型参数只能出去,不能进来(它比这更令人费解,但它可以理解这个想法)。典型的例子?IEnumerable<out T>? 为什么?因为无法T在 an中输入 a IEnumerable<T>,所以这是合法的:

var tigers = new List<Tiger>();
IEnumerable<Animal> animals = tigers;

但这不是:

var tigers = new List<Tiger>();
List<Animal> animals = tigers;

在您的场景中,协变接口似乎不是一种选择,因此您可能需要重新考虑您的方法。你似乎试图在你的类型系统中表达太多,以至于它与你作斗争。


推荐阅读