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?
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());