4

我已经完成了Karl Shiffet 的“InTheBox”WPF 培训,发现它是学习 WPF 的绝佳资源。它确实带来的一件事是使用依赖注入和 Unity 容器。这是向我提出一些问题的代码部分:

public partial class App : Application {

    protected override void OnStartup(StartupEventArgs e) {

        IUnityContainer container = new UnityContainer();

        container.RegisterType<IDialogService, ModalDialogService>(
            new ContainerControlledLifetimeManager());

        container.RegisterType<IEventRepository, EventRepository>(
            new ContainerControlledLifetimeManager());

        MainWindow window = container.Resolve<MainWindow>();

        window.DataContext = container.Resolve<MainWindowViewModel>();

        window.Show();
    }
}

依赖项被注册到 UnityContainer 中,然后由 UnityContainer 注入到 MainWindowViewModel 中。我的问题是为什么要使用容器?就依赖注入而言,为什么不直接使用以下实现相同目的的代码:

protected override void OnStartup(StartupEventArgs e)
{
    IDialogService dialogService = new ModalDialogService();
    IEventRepository eventRepository = new EventRepository();

    MainWindow window = new MainWindow();
    window.DataContext = 
        new MainWindowViewModel(eventRepository, dialogService);
    window.Show();
}

我仍在将依赖项从组合根注入到构造函数中,因此在这种情况下我认为使用 UnityContainer 没有任何好处。

我很欣赏它的存在显然是有原因的,但是有人可以解释在这种情况下它是否会增加任何东西吗?另外,是否还有另一种情况,使用这样的容器真的很容易?

4

2 回答 2

5

在像这样的简单情况下使用 DI 容器对您来说并没有多大用处。当事情变得更复杂时,它开始变得更有意义,并且它也最大限度地减少了依赖关系变化的影响。

例如,您有一个 ILoggingService,您的所有依赖项现在都在使用它。当使用像 Unity 这样的 DI 容器时,您只需添加一行代码。

    protected override void OnStartup(StartupEventArgs e)
    {
        IUnityContainer container = new UnityContainer();
        container.RegisterType<IDialogService, ModalDialogService>();
        container.RegisterType<IEventRepository, EventRepository>();
        container.RegisterType<ILoggingService, LoggingService>(); // added

        MainWindow window = container.Resolve<MainWindow>();
        window.DataContext = container.Resolve<MainWindowViewModel>();
        window.Show();
    }

自己做的时候,要加一行代码,修改3行代码。

    protected override void OnStartup(StartupEventArgs e)
    {
        ILoggingService loggingService = new LoggingService(); // added
        IDialogService dialogService = new ModalDialogService(loggingService); // modified
        IEventRepository eventRepository = new EventRepository(loggingService); // modified

        MainWindow window = new MainWindow();
        window.DataContext = new MainWindowViewModel(eventRepository, dialogService, loggingService); // modified
        window.Show();
    }

当使用可以扫描类型以注册的更高级的容器时,您可能不必更改组合根中的任何代码。这是使用 AutoFac 的示例。

    protected override void OnStartup(StartupEventArgs e)
    {
        var builder = new ContainerBuilder();
        var assembly = Assembly.GetExecutingAssembly();
        builder.RegisterAssemblyTypes(assembly)
               .AsSelf()
               .AsImplementedInterfaces();
        var container = builder.Build();

        MainWindow window = container.Resolve<MainWindow>();
        window.DataContext = container.Resolve<MainWindowViewModel>();
        window.Show();
    }
于 2013-06-06T12:42:04.800 回答
2

你说的对。你的第二个例子是使用 Mark Seemann 可能称之为“穷人的 DI”的东西。它仍然是 DI,但你自己做。

当您开始管理许多不同类型和功能的注入时,IoC 容器就会发挥自己的作用,例如生活方式管理和按惯例注册类型会成为大量的劳动力节省者。

正如您所建议的,对于具有最少依赖管理的较小任务,它们可能是矫枉过正的。

如果您想了解更多信息,我强烈推荐 Seemann 的书和博客。恕我直言,他比任何人都更好地解释了这个话题。

于 2013-06-06T10:43:56.380 回答