.NET默认DI如何支持C# init关键字修饰属性的依赖注入
核心原因
.NET 内置默认依赖注入(DI)容器仅原生支持构造函数注入、方法参数注入两种注入模式,不会自动解析并为init/普通set属性填充依赖实例,这是容器的设计选择,和init关键字本身不存在兼容性冲突。
首先澄清常见认知误区:init属性并非只能通过对象初始化器赋值。它的访问规则是仅允许在对象初始化阶段(实例构造函数执行过程、对象初始化器表达式)赋值,初始化完成后外部无法修改,和传统readonly私有字段的不可变效果几乎一致,完全可以和构造函数注入搭配使用,不需要更换容器也不需要额外配置。
推荐方案(无额外依赖,兼容默认DI)
直接在构造函数中为init属性赋值即可,比传统私有字段+构造函数赋值的写法更简洁,还保留了依赖不可修改的特性:
// 无需声明单独的私有后备字段,init自动保证注入后属性不会被意外篡改 public IContactsProcessService ContactProcessService { get; init; } public WebApiController(IContactsProcessService contactProcessService) { // 构造函数属于init允许的赋值阶段,语法完全合法 ContactProcessService = contactProcessService; }
这种写法的优势:
- 完全兼容.NET默认DI机制,不需要引入第三方组件
- 保留
init的不可变特性,服务注入后不会被意外修改,和原有readonly字段效果一致 - 比传统写法少了私有字段声明、字段逐行赋值的冗余代码,可读性更高
- 保留构造函数注入的依赖校验优势:容器创建实例时就会检查依赖是否注册,缺失依赖会直接抛出异常,不会出现运行时才触发的空引用问题
可选方案(零构造函数写法)
如果希望完全省略构造函数,直接通过属性注入填充依赖,可以替换默认DI容器为支持属性注入的第三方实现,比如Autofac、DryIoc等,这类容器都支持配置规则,自动为标记为public的init属性注入对应实例。
注意:这种写法存在固有缺陷:属性注入不会在实例创建时强制校验依赖是否存在,若依赖未注册,会在首次调用属性时才抛出空引用异常,排查成本更高,非特殊场景不优先推荐。
内容的提问来源于stack exchange,提问作者TryHard
相关产品推荐
相关产品推荐

