Autofac 和逆变:解析为更多派生类型
Posted
技术标签:
【中文标题】Autofac 和逆变:解析为更多派生类型【英文标题】:Autofac and contravariance : resolving to more derived type 【发布时间】:2017-03-19 18:27:25 【问题描述】:正在编写一个通用消息处理程序,需要通过 AutoFac 获取各种消息处理程序。消息处理程序的基本定义是:
public interface IMessageHandler<in TMessage> :
IMessageHandler
where TMessage : IMessage
Task<IMessageResult> Handle(TMessage message);
我还定义了一个标记接口,以便可以在 AutoFac 中轻松注册这些接口
public interface IMessageHandler
一个示例消息处理程序是:
public class CreatedEventHandler : IMessageHandler<CreatedEvent>
public Task<IMessageResult> Handle(CreatedEvent message)
// ...
这些都是通过 Autofac 很好地注册的,使用
builder.RegisterAssemblyTypes(assemblies)
.Where(t => typeof(IMessageHandler).IsAssignableFrom(t))
.Named<IMessageHandler>(t => t.Name.Replace("Handler", string.Empty))
.InstancePerLifetimeScope();
这一切都很好。但是,当我需要解决处理程序时,我遇到了问题
// handler returned is non null and of type marker interface IMessageHandler
var handler = container.Resolve("CreatedEvent");
// This is null. I just can't understand why
var createdEventHander = handler as IMessageHandler<IMessage>;
为什么上面的强制转换返回 null?即使在IMessageHandler<>
接口中定义了逆变。
如何解决合适的处理程序?
谢谢
【问题讨论】:
这与 Autofac 无关。这就是变异在 C#/CLR 中的工作原理。 【参考方案1】:哎呀!
// Covariance
handler as IMessageHandler<IMessage>;
您的handler
有一个通用参数,它不是IMessage
,而是IMessage
实现。因此,这是协方差(你正在向上转换一个通用参数)。
由于我不了解您的实际软件架构,因此无法为您提供解决方案。至少,您知道为什么整个演员阵容都会导致null
。
不费吹灰之力的可能解决方案...
您的消息处理程序既可以实现IMessageHandler<ConcreteEvent>
,也可以实现一个新的非泛型接口IMessageHandler
:
public interface IMessageHandler
Task<IMessageResult> Handle(IMessage message);
public interface IMessageHandler<TMessage> : IMessageHandler
where TMessage : IMessage
Task<IMessageResult> Handle(TMessage message);
public class CreatedEventHandler : IMessageHandler<CreatedEvent>
public Task<IMessageResult> Handle(CreatedEvent message)
// ...
// I would implement the non-generic Handle(IMessage) explicitly
// to hide it from the public surface. You'll access it when successfully
// casting a reference to IMessageHandler
Task<IMessageResult> IMessageHandler.Handle(IMessage message)
return Handle((CreatedEvent)message);
现在整个演员表都可以工作了,因为您的类将显式实现IMessageHandler<IMessage>
。
为了避免重复自己太多,你可以实现一个抽象类:
public abstract class MessageHandler<TMessage> : IMessageHandler<TMessage>
where TMessage : IMessage
public abstract Task<IMessageResult> Handle(TMessage message);
// I would implement the non-generic Handle(IMessage) explicitly
// to hide it from the public surface. You'll access it when successfully
// casting a reference to IMessageHandler
Task<IMessageResult> IMessageHandler.Handle(IMessage message)
return Handle((TMessage)message);
最后,您的具体消息处理程序如下所示:
public class CreatedEventHandler : MessageHandler<CreatedEvent>
public Task<IMessageResult> Handle(CreatedEvent message)
// ...
也就是说,您的演员可以只转换为handler as IMessageHandler
。
【讨论】:
这看起来是一个优雅的解决方案。但是,我在 MessageHandler 抽象类上遇到编译错误:'MessageHandler以上是关于Autofac 和逆变:解析为更多派生类型的主要内容,如果未能解决你的问题,请参考以下文章