如何使用Autofac实现WinForms UserControl等派生类的属性自动注入
实现方案
1. 中间件代码修正
你原有实现存在三个核心问题:
- 管道阶段选择错误:
ParameterSelection阶段目标实例尚未构造完成,无法访问实例属性 - 判断对象错误:
context.Service是Autofac的服务标识对象,不是解析出来的实例 - 缺少递归扫描嵌套控件属性的逻辑
正确的中间件实现如下:
using System.Reflection; using Autofac.Core.Resolving; using Autofac.Core.Resolving.Pipeline; public class AutofacUserControlPropertyInjectionMiddleware : IResolveMiddleware { private static readonly List<Type> InjectTargetBaseTypes = new List<Type> { typeof(UserControl), typeof(BarItem) }; public PipelinePhase Phase => PipelinePhase.Activation; public void Execute(ResolveRequestContext context, Action<ResolveRequestContext> next) { // 先执行后续管道逻辑,确保实例已经完成构造 next(context); // 仅处理顶层Form实例的注入扫描 if (context.Instance is Form topForm) { var processedInstances = new HashSet<object>(); InjectRecursive(context, topForm, processedInstances); } } private static void InjectRecursive(ResolveRequestContext context, object instance, HashSet<object> processed) { if (instance == null || processed.Contains(instance)) return; processed.Add(instance); // 先为当前实例注入容器中匹配的服务属性 context.InjectProperties(instance); // 扫描所有公共可写属性 var properties = instance.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance) .Where(p => p.CanWrite && p.GetGetMethod() != null && p.GetSetMethod() != null); foreach (var prop in properties) { var propValue = prop.GetValue(instance); if (propValue == null) continue; // 判断属性类型是否属于需要注入的控件类型 var propType = propValue.GetType(); if (InjectTargetBaseTypes.Any(baseType => baseType.IsAssignableFrom(propType))) { // 递归处理子控件的属性注入 InjectRecursive(context, propValue, processed); } } } }
2. 注册中间件到Autofac容器
在容器构建时,将该中间件应用到所有顶层Form类型的注册项上即可:
var builder = new ContainerBuilder(); // 单个注册Form示例 builder.RegisterType<MyModalDialog>().AsSelf(); // 批量注册所有Form子类示例 // builder.RegisterAssemblyTypes(typeof(Program).Assembly) // .Where(t => typeof(Form).IsAssignableFrom(t)) // .AsSelf(); // 为所有Form类型的注册项添加注入中间件 builder.RegisterCallback(registry => { foreach (var registration in registry.Registrations) { if (typeof(Form).IsAssignableFrom(registration.Activator.LimitType)) { registration.PipelineBuilder.Use(new AutofacUserControlPropertyInjectionMiddleware()); } } }); var container = builder.Build();
3. 注意事项
- 所有需要被扫描注入的UserControl、BarItem必须以公共可写属性的形式暴露在父控件/Form中
- 递归逻辑加入了已处理实例的哈希集,避免嵌套控件互相引用导致的死循环
- 控件默认构造函数完全保留,不影响Visual Studio窗体设计器的正常使用
内容的提问来源于stack exchange,提问作者void.pointer
相关产品推荐
相关产品推荐

