我不会注入(有效的)您的容器,因为这是一种称为服务定位器的控制反转的低级形式,其缺陷可以通过您当前的情况来总结:您最终需要将容器注入到所有内容中。
相反,您需要从“这个组件负责创建什么?它需要做什么?”的角度来处理问题。
正如 Lonni-Loki 所提到的,一个选择是注入一个完全形成的控件,但我不同意这一点:如果主视图有责任创建这个子组件,那么它应该创建它 - 但为了促进这一责任,你应该将主视图与服务/模型/等一起注入,然后需要传递或以其他方式用于创建它。 Autofac 的工厂方法存根非常适合此目的:
例如,如果子视图需要 IFooViewModel,您可以使用 Func
(或 Func,根据您的需要)
一个方便的经验法则是,在考虑类 X 时,首先在任何地方“新建”任何内容,然后将其传递到构造函数中。现在看看代码并问自己“如果我想要类 X 的实例,我需要传递构造函数什么?”这些是你的依赖。
让我们举一个更实际的例子......假设你有一个像这样的结构:
应用程序创建主窗口
MainWindow创建SubView1,需要IMainWindowViewModel
SubView1需要ISubView1Model,IFooService
子视图1创建子视图2
SubView2需要ISubView2Model、IBarService
所以我们的演员看起来像:
public MainWindow(IMainWindowViewModel viewModel,
Func<SubView1> subView1Factory)
public SubView1(ISubView1Model viewModel,
IFooService fooService,
Func<IFooService, SubView2> subView2Factory)
public SubView2(
ISubView2ModelViewModel viewModel,
IBarService barService)
设置容器时,您会看到类似这样的内容:
(注意,我使用各种 IoC 容器,所以我的 Autofac 语法可能很生疏)
var builder = new ContainerBuilder();
// Simple bit, register implementations for viewmodel, services
builder.RegisterType<MainWindowViewModel>.As<IMainWindowViewModel>();
builder.RegisterType<SubView1Model>.As<ISubView1Model>();
builder.RegisterInstance<FooService>.As<IFooService>();
// ok, lemme see if I can remember expression syntax...
// Simple case: 'static' resolution of subview
// We want a func that takes no args and returns us a fully-initialized
// SubView1
builder.Register<Func<SubView1>>(context =>
{
// Since all the bits of a subview1 are registered, simply
// resolve it and return
var view = context.Resolve<SubView1>();
return () => view;
});
// Complicated case - lets say this viewmodel depends
// on foo service, which it uses to determine which
// bar service to use
builder.Register<Func<IFooService, SubView2>>(context =>
{
// and our view model
var vm = context.Resolve<ISubView2ViewModel>();
return (service) =>
{
var barService = new BarService(service);
return new SubView2(vm, barService);
};
});
IoC 容器最光荣的(在我看来,唯一的)用途是“你根据我已经告诉你的内容弄清楚如何获取所有位”部分 - 否则,你最好使用手动注入,在其中你手动传递依赖项。也就是说,我们潜在的复杂的 MainWindow 构造现在只是:
container.Resolve<MainWindow>();
我希望我在代码中没有犯太多的打字错误/错误,但我已经有一段时间没有使用 Autofac 了。