关于c#:IoC / DI容器,工厂和运行时类型创建

IoC / DI containers, factories and runtime type creation

我最近了解了DI框架Guice和Ninject,并希望在我的一些新项目中使用它们。

虽然我熟悉一般的依赖注入概念,并且知道如何使用这些框架来构建对象图,但是在涉及动态应用程序行为时,我却很难应用IoC。

请考虑以下示例:

  • 当应用程序启动时,将显示主窗口。
  • 当用户单击主面板时,将打开一个上下文菜单。
  • 根据用户的选择,将创建一个新的用户控件并将其显示在鼠标位置。
  • 如果用户最终决定关闭该应用程序,将显示一个确认框,并且在确认后将关闭主窗口。

虽然很容易将主窗口的View连接到Presenter / ViewModel,然后将其绑定到域逻辑,但我不明白如何干净地(就IoC而言)完成以下任务:

  • 动态实例化具体的UI控件(例如IGreenBoxViewIRedImageView <-JConcreteGreenBoxViewJConcreteRedImageView),而无需使用任何类型的服务定位器模式(例如再次请求IoC)

    • 以此为基础,创建一个新的模型,演示者并查看实例
  • 类似地,实例化一个新的具体对话框,例如JOptionPane在运行时

我已经看到了一些使用抽象工厂的解决方案,但是老实说,他们并没有完全理解它们。看来,这样的解决方案将导致某些(视图域,演示者域等)内部类型暴露给构造根,进而暴露给整个世界。

所以-我该怎么做?


如果可以重用控件,则可以在使用控件的地方进行构造函数注入。否则,您必须注入工厂:

1
2
3
4
5
public interface IControlFactory
{
     IGreenBoxView CreateGreenBoxView();
     IRedImageView CreateRedImageView();
}

并将其注入需要创建此控件的位置。

实现转到容器配置。在那里,您可以将容器注入到实现中。一些容器提供了自动实现该工厂的功能。例如,在Ninject中:

1
Bind<IControlFactory>().ToFactory();

请参阅https://github.com/ninject/ninject.extensions.factory/wiki


对于那些希望使用Unity进行类似操作(而不是Ninject)的人,我创建了一个扩展,该扩展使您无需声明接口即可创建工厂:UnityMappingFactory @ GitHub

您只需在正常的自举过程中将映射添加到您注册类的位置即可...

1
2
3
4
5
6
7
8
//make sure to register the output...
container.RegisterType<IImageWidgetViewModel, ImageWidgetViewModel>();
container.RegisterType<ITextWidgetViewModel, TextWidgetViewModel>();

//define the mapping between different class hierarchies...
container.RegisterFactory<IWidget, IWidgetViewModel>()
.AddMap<IImageWidget, IImageWidgetViewModel>()
.AddMap<ITextWidget, ITextWidgetViewModel>();

然后,您只需在CI的构造函数中声明映射工厂接口,并使用其Create()方法...

1
2
3
4
5
6
7
8
9
10
public ImageWidgetViewModel(IImageWidget widget, IAnotherDependency d) { }

public TextWidgetViewModel(ITextWidget widget) { }

public ContainerViewModel(object data, IFactory<IWidget, IWidgetViewModel> factory)
{
    IList<IWidgetViewModel> children = new List<IWidgetViewModel>();
    foreach (IWidget w in data.Widgets)
        children.Add(factory.Create(w));
}

作为额外的好处,在创建对象的过程中,映射类的构造函数中的任何其他依赖关系也将得到解决。 (也分享了此Stackoverflow帖子的答案)