Словарь и интерфейс нескольких ограниченных универсальных типов - PullRequest
0 голосов
/ 28 октября 2018

Я конвертирую проект Java в C #, и у меня возникают следующие проблемы:

IMessageHandler

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

LoginRequestHandler

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

    }
}

MessageHandlerRegistry

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

Я хотел бы сделать следующее:

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

Это дает мне ошибку компиляции, говорящую, что это должен быть тип IMessageHandler.

Яне уверен, почему это не работает, так как LoginRequestHandler реализует только производные типы того, что указано в интерфейсе.

Есть предложения, как это решить?

Ответы [ 2 ]

0 голосов
/ 28 октября 2018

Хорошо, это запрещено, потому что это небезопасно.А?Как может следующее не быть безопасным для типа:

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 может обрабатывать предоставленные типы аргументов ...

Итак, теперь мы попали в ужасную ситуацию;выполнив совершенно законные шаги, мы только что нарушили систему типов, потому что почему-то мы попросили LoginRequestHandler обработать LogOutRequest.

Очевидно, поскольку вы испытали мучительный опыт из первых рук, не все шагизаконны;это ссылочное преобразование фактически недопустимо: (IMessageHandler<IPeerAttachment, IMessage>)loginRequestHandler

Чтобы это преобразование работало, дисперсия типа IMessageHandler должна быть ковариантной, что означает, что универсальные аргументы могут только выходить, а не в (это немного более замысловато, чем это, но это передает идею).Канонический пример?IEnumerable<out T>?Зачем?Поскольку невозможно ввести T в IEnumerable<T>, поэтому это допустимо:

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

Но это не так:

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

В вашем сценарииковариантный интерфейс, кажется, не является выбором, поэтому вам, вероятно, нужно пересмотреть свой подход.Похоже, вы пытаетесь выразить слишком много в своей системе типов до такой степени, что она борется с вами.

0 голосов
/ 28 октября 2018

То, что вы пытаетесь сделать, в корне несовместимо с системой типов, которая не является ошибочной в отношении общих отклонений, как, например, система типов Java.

Если вы смогли назначить LoginRequestHandlerдля переменной типа IMessageHandler<IPeerAttachment, IMessage> тогда вы сможете вызывать метод HandleMessage с любыми типами, производными от IPeerAttachment и IMessage, что приведет к ошибке времени выполнения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...