4

Suppose I have the following code:

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

I enabled contravariance for the interface IMessageProcessor. Why does MessageProcessors.Add(typeof(RRMessage), msgProcessor); cause a compile time error:

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

It seems like it should be able to convert because RRMessageProcessor:IMessageProcessor<RRMessage:IBaseMessage>> How can I get this to work?

Denis
  • 11,796
  • 16
  • 88
  • 150
  • `IMessageProcessor` is contravariant in `T` so you can only assign `IMessageProcessor` to an `IMessageProcessor` for some subtype `TSub` of `RRMessage`. The conversion you want would be unsafe since it would allow you to call `RRMessageProcessor.Process(new BaseMessage())` when it requires an `RRMessage` parameter. – Lee Mar 04 '16 at 16:34
  • How would you rewrite this? – Denis Mar 04 '16 at 16:37
  • @Denis Best you can do here is make your dictionry `Dictionary`. – juharr Mar 04 '16 at 16:43
  • @juhar, I don't understand, I would like to get a dictionary of a message type and what processor can process that message. – Denis Mar 04 '16 at 16:44

1 Answers1

2

IMessageProcessor<in T> is contravariant in T, not covariant. That means the following is allowed:

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

what you are trying to do is statically unsafe since it would allow you do to:

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

so you need to maintain safety dynamically instead, which you seem to be attempting with the dictionary Type -> Processor. You can therefore create an unsafe wrapper type:

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

You can then construct your list with these wrappers while maintaining the inner processors typed as you want e.g.

public Dictionary<Type, IMessageProcessor<IBaseMessage>> MessageProcessors = new Dictionary<Type, IMessageProcessor<IBaseMessage>>();
MessageProcessors.Add(new ProcessorWrapper<RRMessage>(new RRMessageProcessor());
Lee
  • 142,018
  • 20
  • 234
  • 287
  • Can you please show how you would use your ProcessorWrapper in a dictionary and how you would add RRMessageProcessor to that dictionary. I tried to create a dictionary but of what? Dictionary>? – Denis Mar 04 '16 at 16:59