2016-03-04 6 views
4

Предположим, у меня есть следующий код:Не похоже, чтобы получить Generic ковариации/контрвариация работать

public interface IBaseMessage { } 

public interface IMessageProcessor<in T> where T : IBaseMessage { 
      void Process(T msg); 
     } 

public class RRMessage : IBaseMessage { 
      //something here 
     } 

public class BaseMessageProcessor { 
     //something here 
     } 

public class RRMessageProcessor : BaseMessageProcessor, IMessageProcessor<RRMessage> { 
      public void Process(RRMessage msg) { 
       Console.WriteLine("Processed RRMessage"); 
      } 
     } 

public Dictionary<Type, IMessageProcessor<IBaseMessage>> MessageProcessors = new Dictionary<Type, IMessageProcessor<IBaseMessage>>(); 

[Test] 
public void Test1() { 
    var msgProcessor = new RRMessageProcessor(); 
    MessageProcessors.Add(typeof(RRMessage), msgProcessor); 
    } 

Я позволил контравариации для интерфейса IMessageProcessor. Почему MessageProcessors.Add(typeof(RRMessage), msgProcessor); причина компиляции ошибки времени:

Argument 2: cannot convert from 'RRMessageProcessor' to 'IMessageProcessor<IBaseMessage>'

Похоже, он должен быть в состоянии преобразовать, потому что RRMessageProcessor:IMessageProcessor<RRMessage:IBaseMessage>> Как я могу получить эту работу?

+0

'' IMessageProcessor контравариантен в 'Т *, так что вы можете назначить только' 'IMessageProcessor Ань' 'IMessageProcessor для некоторого подтипа' 'TSub' из RRMessage'. Преобразование, которое вы хотите, будет небезопасным, поскольку оно позволит вам вызывать 'RRMessageProcessor.Process (new BaseMessage())', когда для этого требуется параметр «RRMessage». – Lee

+0

Как бы вы переписали это? – Denis

+0

@Denis Лучшее, что вы можете сделать здесь, это сделать свою словари «Словарь <Тип, BaseMessageProcessor>». – juharr

ответ

2

IMessageProcessor<in T> является контравариантным в T, а не ковариантным. Это означает следующее разрешено:

class RRSubtype : RRMessage {} 
IMessageProcessor<RRSubtype> p = new RRMessageProcessor(); 

, что вы пытаетесь сделать, это статический небезопасно, так как это позволит вам сделать, чтобы:

class NotRRMessage : IBaseMessage { } 
IMessageProcessor<IBaseMessage> p = new RRMessageProcessor(); 
p.Process(new NotRRMessage()); 

так что вам нужно для поддержания безопасности динамически, а не, что вы, кажется, для поиска со словарем Type -> Processor. Таким образом, вы можете создать небезопасный тип обертки:

public class ProcessorWrapper<T> : IMessageProcessor<IBaseMessage> { 
    private readonly IMessageProcessor<T> inner; 
    public ProcessorWrapper(IMessageProcessor<T> inner) { this.inner = inner; } 

    public void Process(IBaseMessage msg) 
    { 
     if(msg is T) { inner.Process((T)msg); } 
     else throw new ArgumentException("Invalid message type"); 
    } 
} 

Вы можете построить свой список с этими обертками, сохраняя при этом внутренние процессоры типизированных, как вы хотите, например,

public Dictionary<Type, IMessageProcessor<IBaseMessage>> MessageProcessors = new Dictionary<Type, IMessageProcessor<IBaseMessage>>(); 
MessageProcessors.Add(new ProcessorWrapper<RRMessage>(new RRMessageProcessor()); 
+0

Можете ли вы показать, как использовать словарь ProcessorWrapper в словаре и как добавить RRMessageProcessor в этот словарь. Я пытался создать словарь, но что? Словарь >? – Denis

+0

@Denis - См. Обновление. – Lee

+0

Отлично, спасибо! – Denis