Autofac类型分配异常:Factory<Entity>无法转为IFactory<IEntity>的解决问询
问题场景
先看你定义的类和接口:
interface IFactory<T>{} class Factory<T> : IFactory<T>{} interface IEntity{} class Entity : IEntity{ }
你想通过Autofac把IFactory<IEntity>解析为Factory<Entity>,注册代码是:
b.RegisterType<Factory<Entity>>().As<IFactory<IEntity>>();
结果抛出了异常:
The type 'Factory
1[Entity]' is not assignable to service 'IFactory1[[IEntity]]'
你还提到了解过RegisterGeneric但觉得不适用,并且实际场景中可能需要为IFactory<IEntity>配置不同的组件。
异常原因
这个问题的核心是C#泛型接口的默认不变性。默认情况下,泛型接口IFactory<T>中的T是不变的——也就是说,即使Entity实现了IEntity,IFactory<Entity>和IFactory<IEntity>也被视为完全不同的类型,不能互相赋值。
举个通俗的例子:你不能把List<Entity>直接赋值给List<IEntity>,道理是一样的,除非接口显式声明支持协变或逆变。
解决方法
针对你的需求,有两种可行的解决思路:
1. 修改泛型接口支持协变
如果你的业务场景允许(IFactory<T>主要是输出T类型的对象,而不是作为参数输入),可以给IFactory<T>的泛型参数加上out关键字,声明它是协变的:
interface IFactory<out T>{}
这样修改后,Factory<Entity>就可以安全地向上转换为IFactory<IEntity>,你的原注册代码就能正常工作了。
2. 不修改接口,通过Autofac的委托注册实现
如果因为某些原因不能修改IFactory<T>接口,你可以用Autofac的委托注册方式,手动完成类型转换:
b.Register(ctx => (IFactory<IEntity>)new Factory<Entity>()) .As<IFactory<IEntity>>();
如果Factory<Entity>有构造参数需要Autofac注入,还可以利用上下文解析依赖:
b.Register(ctx => { var factory = ctx.Resolve<Factory<Entity>>(); return (IFactory<IEntity>)factory; }).As<IFactory<IEntity>>();
这种方式相当于手动告诉Autofac如何将Factory<Entity>转换为IFactory<IEntity>,绕过了默认的类型检查。
3. 针对多实现场景的处理
你提到实际场景中可能需要为IFactory<IEntity>配置不同的组件,这种情况下可以给注册的组件加上命名,解析时指定名称即可:
// 注册第一个实现 b.Register(ctx => (IFactory<IEntity>)new Factory<Entity>()) .Named<IFactory<IEntity>>("entityFactory"); // 注册第二个实现(比如Factory<OtherEntity>,假设OtherEntity也实现了IEntity) b.Register(ctx => (IFactory<IEntity>)new Factory<OtherEntity>()) .Named<IFactory<IEntity>>("otherEntityFactory"); // 解析时指定名称 var factory = container.ResolveNamed<IFactory<IEntity>>("entityFactory");
至于你提到的RegisterGeneric,它主要用于注册泛型模板(比如注册Factory<T>作为所有IFactory<T>的实现),确实不适合你这种“特定泛型参数映射”的场景,你的判断是对的。
内容的提问来源于stack exchange,提问作者mike

