64

我决定开始使用 Ninject 并面临一个问题。假设我有以下情况。我有一个IService接口和 2 个实现这个接口的类。而且我还有一个类,它有一个获取 IService 和一个int的构造函数。如何使用 Ninject 创建此类的实例(我不想硬连线这个 int,我想在每次获得实例时传递它)?

这是一些说明情况的代码:

interface IService
{
    void Func();
}

class StandardService : IService
{
    public void Func()
    {
        Console.WriteLine("Standard");
    }
}

class AlternativeService : IService
{
    public void Func()
    {
        Console.WriteLine("Alternative");
    }
}


class MyClass
{
    public MyClass(IService service, int i)
    {
        this.service = service;
    }

    public void Func()
    {
        service.Func();
    }

    IService service = null;
}
class Program
{
    static void Main(string[] args)
    {
        IKernel kernel = new StandardKernel(new InlineModule(
            x => x.Bind<IService>().To<AlternativeService>(),
            x => x.Bind<MyClass>().ToSelf()));

        IService service = kernel.Get<IService>();

        MyClass m = kernel.Get<MyClass>();
        m.Func();
    }
}
4

1 回答 1

93

With.ConstructorArgument为此目的存在于 1.0 中。在 2.0 中,语法略有变化:- With.Parameters.ConstructorArgument 与 ninject 2.0

有关如何使用上下文、提供程序和参数更正确地传递此类内容的更多详细信息和示例,请参阅将值注入到注入的依赖项中。

编辑:由于史蒂文选择假装我的评论无关紧要,我最好用一些例子(对于 2.0)说明我在说什么:

MyClass m = kernel.Get<MyClass>( new ConstructorArgument( "i", 2) );

在我看来,这非常清楚,并且准确地说明了正在发生的事情。

如果您处于可以以更全局的方式确定参数的位置,则可以注册提供者并这样做:

class MyClassProvider : SimpleProvider<MyClass>
{
    protected override MyClass CreateInstance( IContext context )
    {
        return new MyClass( context.Kernel.Get<IService>(), CalculateINow() );
    }
}

并像这样注册它:

x => x.Bind<MyClass>().ToProvider( new MyClassProvider() )

请注意,这CalculateINow()就是您在第一个答案中放入逻辑的地方。

或者像这样使它更复杂:

class MyClassProviderCustom : SimpleProvider<MyClass>
{
    readonly Func<int> _calculateINow;
    public MyClassProviderCustom( Func<int> calculateINow )
    {
        _calculateINow = calculateINow;
    }

    protected override MyClass CreateInstance( IContext context )
    {
        return new MyClass( context.Kernel.Get<IService>(), _calculateINow() );
    }
}

你会这样注册:

x => x.Bind<MyClass>().ToProvider( new MyClassProviderCustom( (  ) => new Random( ).Next( 9 ) ) )

更新:较新的机制表现出比上述更少的样板文件的改进模式Ninject.Extensions.Factory,见: https ://github.com/ninject/ninject.extensions.factory/wiki

如前所述,如果您需要每次传递不同的参数并且依赖图中有多个级别,则可能需要执行类似的操作

最后一个考虑因素是,因为您没有指定 a Using<Behavior>,所以它将默认为内核选项中指定/默认的默认值(在示例中),这可能会导致工厂即时TransientBehavior计算的事实[例如,i如果对象被缓存]

现在,澄清评论中被 FUD 和掩盖的其他一些观点。关于使用 DI 需要考虑的一些重要事项,无论是 Ninject 还是其他任何东西:

  1. 通过构造函数注入尽可能多地完成,因此您不需要使用容器特定的属性和技巧。有一篇很好的博客文章叫做Your IoC Container is Showing

  2. 最小化进入容器的代码并请求东西 - 否则您的代码会耦合到 a) 特定容器(CSL 可以最小化)b) 整个项目的布局方式。有很好的博客文章表明 CSL 没有做你认为的那样。这个一般主题被称为服务位置与依赖注入。更新:有关详细和完整的理由,请参阅http://blog.ploeh.dk/2011/07/28/CompositionRoot.aspx 。

  3. 尽量减少使用静态和单例

  4. 不要假设只有一个 [global] 容器,并且只要您需要它就可以像一个很好的全局变量一样要求它。正确使用多个模块并Bind.ToProvider()为您提供管理它的结构。这样每个单独的子系统都可以独立工作,并且您不会将低级组件绑定到顶级组件等。

如果有人想填写我所指的博客的链接,我将不胜感激(尽管它们都已经从 SO 上的其他帖子链接,所以所有这些只是为了目的而引入的重复 UI避免混淆误导性答案。)

现在,如果只有 Joel 能进来并真正让我明白什么是好的语法和/或正确的方法!

更新:虽然从获得的赞成票数量来看,这个答案显然很有用,但我想提出以下建议:

  • 以上感觉有点过时了,老实说,反映了很多不完整的想法,自从阅读.net 中的依赖注入后几乎感觉很尴尬- 现在运行并购买 - 这不仅仅是关于 DI,前半部分是对围绕它的所有架构问题都来自一个在这里花费太多时间在依赖注入标签周围徘徊的人。
  • 立即在 SO 上阅读Mark Seemann 的评分最高的帖子- 您将从每个人那里学到有价值的技术
于 2010-02-09T12:19:45.787 回答