使用Ninject在构造函数中使用其他参数创建实例

时间:2010-02-09 07:55:39

标签: c# ioc-container ninject

我决定开始使用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();
    }
}

1 个答案:

答案 0 :(得分:92)

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

有关更多详细信息以及如何使用上下文,提供程序和参数更正确地传递此类内容的示例,请参阅Inject value into injected dependency

编辑:由于史蒂文选择假装我的评论无关紧要,我最好通过一些例子(对于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

如前所述,if you need to pass a different parameter each time and you have multiple levels in the dependency graph, you might need to do something like this

最后一个考虑是因为你没有指定Using<Behavior>,它将默认为内核选项中指定/默认的默认值(样本中的TransientBehavior)渲染事实,工厂在飞行中计算i [例如,如果对象被缓存]

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

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

  2. 最小化代码进入容器并询问内容 - 否则您的代码将耦合到a)特定容器​​(CSL可以最小化)b)整个项目的布局方式。有很好的博客文章显示CSL没有做你认为它做的事情。这个一般主题称为Service Location vs Dependency Injection。更新:有关详细完整的理由,请参阅http://blog.ploeh.dk/2011/07/28/CompositionRoot.aspx

  3. 尽量减少使用静力学和单身人士

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

  5. 如果有人想填写我所指的博客的链接,我会很感激(虽然所有这些都是关于其他帖子的,但所有这些只是重复的UI已经介绍目的是避免混淆误导性答案。)

    现在,如果只有Joel可以进来并真正让我直截了当地说出了很好的语法和/或正确的方法!

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

    • 以上感觉因为它有点过时而且说实话反映了许多不完整的思维,自从阅读Dependency Injection in .net后几乎感到尴尬 - 现在运行并购买 - 它不只是关于DI,上半部分是完全处理围绕它的所有架构问题,这个人花了太多时间在依赖注入标记周围。
    • 阅读Mark Seemann's top rated posts here on SO right now - 您将从每个人那里学到有价值的技巧