Kal*_*son 16 c# code-generation dependency-injection inversion-of-control winforms
当使用依赖注入(DI)和控制反转(IoC)时,对象通常会有一个构造函数,它接受对象正常运行所需的依赖集.
例如,如果我有一个需要服务来填充组合框的表单,您可能会看到如下所示的内容:
// my files
public interface IDataService {
IList<MyData> GetData();
}
public interface IComboDataService {
IList<MyComboData> GetComboData();
}
public partial class PopulatedForm : BaseForm {
private IDataService service;
public PopulatedForm(IDataService service) {
//...
InitializeComponent();
}
}
Run Code Online (Sandbox Code Playgroud)
这在顶级工作正常,我只是使用我的IoC容器来解决依赖关系:
var form = ioc.Resolve<PopulatedForm>();
Run Code Online (Sandbox Code Playgroud)
但面对生成的代码,这会变得更难.在winforms中,生成组成其余部分类的第二个文件.此文件引用其他组件(如自定义控件),并使用no-args构造函数来创建此类控件:
// generated file: PopulatedForm.Designer.cs
public partial class PopulatedForm {
private void InitializeComponent() {
this.customComboBox = new UserCreatedComboBox();
// customComboBox has an IComboDataService dependency
}
}
Run Code Online (Sandbox Code Playgroud)
由于这是生成的代码,我无法传递依赖项,并且没有简单的方法让我的IoC容器自动注入所有依赖项.
一种解决方案是传递每个子组件的依赖关系,PopulatedForm即使它可能不需要它们直接,例如使用IComboDataService所需的UserCreatedComboBox.然后,我有责任确保通过各种属性或setter方法提供依赖项.然后,我的PopulatedForm构造函数可能如下所示:
public PopulatedForm(IDataService service, IComboDataService comboDataService) {
this.service = service;
InitializeComponent();
this.customComboBox.ComboDataService = comboDataService;
}
Run Code Online (Sandbox Code Playgroud)
另一种可能的解决方案是让no-args构造函数执行必要的解析:
public class UserCreatedComboBox {
private IComboDataService comboDataService;
public UserCreatedComboBox() {
if (!DesignMode && IoC.Instance != null) {
comboDataService = Ioc.Instance.Resolve<IComboDataService>();
}
}
}
Run Code Online (Sandbox Code Playgroud)
这两种解决方案都不是特别好.在生成的代码面前,有哪些模式和替代方法可以更有效地处理依赖注入?我希望看到两种通用解决方案,例如模式,以及特定于C#,Winforms和Autofac的解决方案.
我相信这里没有银弹解决方案.在这种情况下,我会使用属性注入来保留无参数构造函数.另外我个人不喜欢将服务注入到UI类中,我更喜欢在那里注入某种类型的Presenter.然后你有一个属性Presenter,它将由IoC容器设置,在这个属性的setter中你将有你的初始化代码.
在你的两个解决方案中,我不喜欢第二个解决方案,特别是因为在代码中引用了IoC容器,这是不好的IMO.