在 WPF 应用程序中注入:请求某个类型的实例

本文关键字:类型 实例 请求 WPF 应用程序 注入 | 更新日期: 2023-09-27 18:32:27

我正在开发一个WPF应用程序,它有很大一部分业务逻辑和数据处理。根据使用 IoC 的想法,我决定使用 Ninject 作为这部分逻辑和数据处理的依赖注入器。

我已经初始化了 ninject 的内核,并将接口与 App.xaml.cs 文件中的具体类绑定(即默认应用程序类)。到目前为止,一切都很好。

问题是在我的应用程序中的某个地方,我需要类的两个具体实例(我不想在构造函数中作为参数传递)。实际上我需要做的是:

var instance1 = kernel.Get<IClassName>();

问题是我无法访问kernel(Ninject内核的具体实例)。

我知道在 ASP.NET MVC 中我可以做类似的事情

var instance1 = (IClassName)System.Web.Mvc.DependencyResolver.Current.GetService(typeof(IClassName));

我可以使用类似的东西吗?还是我被迫使用服务定位器模式解决问题(我知道对此存在相互矛盾的意见)?

在 WPF 应用程序中注入:请求某个类型的实例

由于您希望每个类使用多个依赖项,因此您应该寻找设计模式而不是 DI 容器来解决问题。

至少有几个选择。下面是设置方案的一些基本框架:

public interface IPresenter
{
    void Present();
}
public interface IConsumer
{
    void DoSomething();
}
public class SomeConsumer : IConsumer
{
    private readonly IPresenter presenter;
    public SomeConsumer(IPresenter presenter)
    {
        if (presenter == null)
            throw new ArgumentNullException("presenter");
        this.presenter = presenter;
    }
    public void DoSomething()
    {
        this.presenter.Present();
    }
}

您可以使用复合模式,然后可以在列表中对演示者进行排序。

public class Presenter1 : IPresenter
{
    public void Present()
    {
        // Do something here
    }
}
public class Presenter2 : IPresenter
{
    public void Present()
    {
        // Do something here
    }
}
public class Presenter3 : IPresenter
{
    public void Present()
    {
        // Do something here
    }
}
public class CompositePresenter : IPresenter
{
    private readonly IPresenter[] presenters;
    public CompositePresenter(IPresenter[] presenters)
    {
        if (presenters == null)
            throw new ArgumentNullException("presenters");
        this.presenters = presenters;
    }
    public void Present()
    {
        // Do nothing except delegate the call to the nested
        // instances. You may need to do some extra work to deal
        // with multiple return values, like add up the values
        // or decide which value works best for the scenario.
        foreach (var presenter in this.presenters)
        {
            presenter.Present();
        }
    }
}

然后像这样连接起来:

var presenter1 = new Presenter1();
var presenter2 = new Presenter2();
var presenter3 = new Presenter3();
var compositePresenter = new CompositePresenter(new IPresenter[] {
    presenter1,
    presenter2,
    presenter3
});
var consumer = new SomeConsumer(compositePresenter);

在 Ninject 中,上面的内容如下所示:

var kernel = new StandardKernel();
kernel.Bind<Presenter1>().ToSelf();
kernel.Bind<Presenter2>().ToSelf();
kernel.Bind<Presenter3>().ToSelf();
kernel.Bind<IPresenter>().To<CompositePresenter>()
    .WithConstructorArgument("presenters", 
        new IPresenter[] { 
            kernel.Get<Presenter1>(), 
            kernel.Get<Presenter2>(),
            kernel.Get<Presenter3>() 
        });
// When SomeConsumer is injected into a constructor, its IPresenter 
// dependency will be wired as shown with the new keyword example above.

或者您可以使用装饰器模式:

public class Presenter1 : IPresenter
{
    public Presenter1(IPresenter innerPresenter)
    {
        if (innerPresenter == null)
            throw new ArgumentNullException("innerPresenter");
        this.innerPresenter = innerPresenter;
    }
    public void Present()
    {
        // Do something here
        // You could make this call conditional
        this.innerPresenter.Present();
        // Or do something here
    }
}
public class Presenter2 : IPresenter
{
    public Presenter2(IPresenter innerPresenter)
    {
        if (innerPresenter == null)
            throw new ArgumentNullException("innerPresenter");
        this.innerPresenter = innerPresenter;
    }
    public void Present()
    {
        // Do something here
        // You could make this call conditional
        this.innerPresenter.Present();
        // Or do something here
    }
}
public class Presenter3 : IPresenter
{
    public Presenter3(IPresenter innerPresenter)
    {
        if (innerPresenter == null)
            throw new ArgumentNullException("innerPresenter");
        this.innerPresenter = innerPresenter;
    }
    public void Present()
    {
        // Do something here
        // You could make this call conditional
        this.innerPresenter.Present();
        // Or do something here
    }
}
public class NullPresenter : IPresenter
{
    public void Present()
    {
        // Do nothing here - this class is a placeholder
        // in case you want to expand the design later
    }
}

然后像这样连接起来:

var nullPresenter = new NullPresenter();
var presenter3 = new Presenter3(nullPresenter);
var presenter2 = new Presenter2(presenter3);
var presenter1 = new Presenter1(presenter2);
var consumer = new SomeConsumer(presenter1);

在 Ninject 中,上面的内容如下所示:

var kernel = new StandardKernel();
kernel.Bind<IPresenter>().To<NullPrenter>().WhenInjectedInto<Presenter3>();
kernel.Bind<IPresenter>().To<Presenter3>().WhenInjectedInto<Presenter2>();
kernel.Bind<IPresenter>().To<Presenter2>().WhenInjectedInto<Presenter1>();
kernel.Bind<IPresenter>().To<Presenter1>();
// When SomeConsumer is injected into a constructor, its IPresenter 
// dependency will be wired as shown with the new keyword example above.

使用设计模式的优点是,最终会得到完全模块化、可扩展甚至独立于 DI 容器的松散耦合代码。

使用 Ninject.Extensions.Factory 来实例化所述依赖项怎么样?您可以注入一个Func<T>,当您稍后调用它时,它将返回一个新实例化的T。如果您只想"首次使用"延迟实例化单个实例,还有Lazy<T>

然后,您还可以定义接口工厂,例如:

public interface IFooFactory
{
    IFoo Create();
}

使用相应的绑定:

Bind<IFooFactory>().ToFactory();
Bind<IFoo>().To<Foo>(); // add Singleton, or ToMethod binding,.. or whatever you require