似乎无法使通用协方差/逆变法工作

时间:2016-03-04 16:30:17

标签: c# generics covariance contravariance

假设我有以下代码:

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);会导致编译时错误:

  

参数2:无法从'RRMessageProcessor'转换为   'IMessageProcessor&LT; IBaseMessage&GT;'

它似乎应该能够转换,因为RRMessageProcessor:IMessageProcessor<RRMessage:IBaseMessage>>我怎样才能让它工作?

1 个答案:

答案 0 :(得分: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());