在Prism/Unity中,Container.Resolve<ShellPresenter>()是什么意思?

18

以下是Prism V2股票交易示例应用程序中StockTraderRIBootstrapper.cs文件的内容:

这两者之间有什么区别:

ShellPresenter presenter = new ShellPresenter();

和这个:

ShellPresenter presenter = Container.Resolve<ShellPresenter>();
  • 我理解第二个例子是将容器视为工厂,直接向其请求“我需要一个类型为ShellPresenter的实例化对象”。
  • 但是如果我需要发送参数,相当于“new ShellPresenter(1, true)”之类的,应该怎么做呢?
  • 由于容器必须告诉它关于ShellPresenter,所以我希望在项目中找到一个地方,将ShellPresenter类注册到容器中,例如,我期望看到:

像这样的内容:

Container.RegisterType<IShellPresenter, ShellPresenter>();

但是我在哪里也没找到它。那么容器如何知道这些类型,以便可以解析它们呢?我在自己的项目中重新构建了这个项目,但出现了“依赖项解析失败”的错误,那么我需要在哪里注册这个依赖项呢?
任何方向/讨论都会有所帮助。
未解释答案:
因此,在引导程序中,当我注册Shell本身时:
protected override void ConfigureContainer()
{
    Container.RegisterType<IShellView, Shell>();
    base.ConfigureContainer();
}

然后容器就可以解析ShellPresenter类型。那么当我注册Shell类型时,ShellPresenter类型是如何注册的呢?

令人惊讶的答案:

好的,事实证明,您不必注册要解析的类型,但您必须注册传递给要解析的类型构造函数的参数(接口)类型,即因为我将IShellView接口注入到我的ShellPresenter的构造函数中,所以我需要注册IShellView类型而不是IShellPresenter类型:

public ShellPresenter(IShellView view) ...

我通过尝试解析类型 Tester 来测试这个功能:
Tester tester = Container.Resolve<Tester>();

只要我将SomeClass注入其构造函数中:
public Tester(ISomeClass someClass)

在我将SomeClass注册到容器之前,我会遇到未解决的依赖错误:

Container.RegisterType<ISomeClass, SomeClass>();

那么它就有效了。这既令人惊讶又具有教育意义。需要沉淀。我要去喝咖啡,思考一会儿。

如果有人能详细解释为什么会这样,将不胜感激。


我认为 RegisterType<>() 方法的零参数副作用是它自动查找映射类型的构造函数依赖项,并在解析时履行它们。您可以通过 API 和配置文件精确配置此处发生的情况,但显然这是默认行为。 - Neil Hewitt
4个回答

12
如果您尝试解析一个具体的类,并且没有注册实例或子类以满足它,那么Unity会为您构造一个具体类的实例,并解析其所有依赖项。
因此,当您请求ShellPresenter并且未注册它时,Unity会使用ShellView作为参数为您创建一个新的ShellPresenter实例。

在阅读这个简单的语句几次之后,你已经理解了相当多! - Metro Smurf

6

您已经了解了基础知识。

对于需要构造函数参数的类型,可以使用重载来解决。或者,您始终可以编写没有参数的构造函数的类型。

DI容器的重点在于您可以配置它们以更改为特定接口解析的类型,而不必重新编译软件。 您提供的配置提供程序的代码示例无法在运行时更改。 这就是为什么大多数依赖注入器允许您在app.config / web.config / 其他外部配置文件中配置它们的原因。 这样,您可以重新配置您的应用程序以注入不同的类型,而无需重新编译,这是像Unity这样的DI框架真正的强大之处。


看,这不是魔法。在你的示例应用程序中,它要么在代码中注册,要么在配置文件中注册。如果你还没有找到它,那是因为你正在错误的地方寻找。 - user1228
另外,请提供演示项目的链接;我无法在任何地方找到它。 - user1228
关于你的评论“这不是魔法”,实际上它确实是魔法;)。Unity支持对未注册类型进行“解析”调用,只要它不是接口/抽象类,就像Ray指出的那样。 - julealgon
@julealgon:现在它可以,你是说...还是说它在09年就能做到这一点了? - user1228
我认为以前也是这样的,但我不确定。然而,在我看来,这在此情境下并不重要,因为我的评论是为了提醒现在阅读问题/答案的人(就像我一样)。说实话,我根本没有注意到帖子上的时间戳。 - julealgon
显示剩余3条评论

1

好的,我不能回答Untiy的问题,但对于Castle Windsor,注册可以在app.config/web.config文件中进行。 还可以在配置XML中添加参数。

这允许您更改对象的实现和配置,而无需重新编译应用程序。


有趣,好的,虽然我在App.config中找不到它,但我正在按照Prism V2指南中的StockTrader演示进行工作。 - Edward Tanguay

1
在Unity中,确实有一个Container.RegisterType<TFrom, TTo>()方法集,可以在运行时注册类型。使用XML配置文件进行此操作可能更常见,但两种方法都可以。
有趣的是,在Unity中没有Container.Resolve<T>(params object[] parameters)这样的方法来解析具有特定构造函数参数值的类型。Unity建立在ObjectBuilder之上,后者是P&P团队用于进行对象构建和连接的库(如果我没记错,它最初是为ObjectSpaces编写的,但现在已经显着增强)。ObjectBuilder使您能够以各种方式注入依赖项,包括通过构造函数。例如,您可以说-将依赖于其的新类型的实例传递到已解析类型的构造函数中;但是该类型也必须注册。您还可以传递已注册类型的实例(已注册的实例/单例等)。但是,据我所知,没有简单地给它一个要传递的值的方法。

我认为这样做在某种程度上违背了IoC的理念,这就是为什么它们没有提供这种功能的原因。理论上,容器应该能够在任何情况下为您提供完整的对象图,因此您永远不需要传递参数,并且使您的对象依赖于除可注入对象依赖项(容器将为您解决)之外的构造函数参数被视为糟糕的设计。

我不能代表Windsor、StructureMap或其他人,他们可能允许您这样做。我甚至不能绝对地说Unity没有办法做到这一点,因为我是相对较新的,但据我所知,基本上构建Unity的Chris Tavares会时不时地出现在这里,所以也许他会来回答这个问题 :-)


Resolve调用中无法指定参数,但是你可以通过使用InjectionConstructor实例在注册本身中控制它们。我已经在一些情况下多次使用过它,你可以通过这种方式传递任何你想要的东西。 - julealgon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接