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

Stu*_*ens 63 c# ninject ioc-container

我决定开始使用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();
    }
}
Run Code Online (Sandbox Code Playgroud)

Rub*_*ink 93

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

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

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

MyClass m = kernel.Get<MyClass>( new ConstructorArgument( "i", 2) );
Run Code Online (Sandbox Code Playgroud)

我的眼睛非常清楚,并准确说明发生了什么.

如果您处于可以以更全局的方式确定参数的位置,则可以注册提供程序并按以下方式执行:

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

并注册如下:

x => x.Bind<MyClass>().ToProvider( new MyClassProvider() )
Run Code Online (Sandbox Code Playgroud)

注意,这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() );
    }
}
Run Code Online (Sandbox Code Playgroud)

你注册的是这样的:

x => x.Bind<MyClass>().ToProvider( new MyClassProviderCustom( (  ) => new Random( ).Next( 9 ) ) )
Run Code Online (Sandbox Code Playgroud)

更新:Ninject.Extensions.Factory扩展中体现了更新的机制,其中显示了大大改进的模式,并且具有比上述更少的样板,请参阅:https: //github.com/ninject/ninject.extensions.factory/wiki

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

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

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

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

  2. 最小化代码进入容器并询问内容 - 否则您的代码将耦合到a)特定容器​​(CSL可以最小化)b)整个项目的布局方式.有很好的博客文章显示CSL没有做你认为它做的事情.此常规主题称为服务位置与依赖注入.更新:有关详细和完整的基本原理,请参阅http://blog.ploeh.dk/2011/07/28/CompositionRoot.aspx.

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

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

如果有人想填写我所指的博客的链接,我会很感激(虽然所有这些都已经在其他帖子中链接了所以这些只是重复的UI已经引入了目标避免混淆误导性答案.)

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

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

  • 上面的感觉因为它有点过时而且说实话反映了许多不完整的思维,自从阅读.net中的依赖注入以来几乎感到尴尬- 现在运行并购买它 - 它不仅仅是关于DI,上半部分是一个完整的处理所有围绕它的架构问题来自于一个花费太多时间在这里依赖依赖注入标记的人.
  • 立即阅读Mark Seemann在SO上的评分最高的帖子 - 您将从每个人那里学到有价值的技巧