首页 文章

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

提问于
浏览
62

我决定开始使用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 回答

  • 90

    为此目的, 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还是其他任何东西:

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

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

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

    • Don 't assume there is only one [global] container and that it'可以随时随地要求它就像一个漂亮的全局变量 . 正确使用多个模块和 Bind.ToProvider() 为您提供了一个管理它的结构 . 这样,每个独立的子系统都可以独立工作,并且您不会将低级组件与顶级组件绑定等 .

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

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

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

    • 上面的感觉因为它有点过时而且说实话反映了很多不完整的想法,自从阅读以来几乎感到尴尬Dependency Injection in .net - 现在运行并购买它 - 它不仅仅是关于DI,上半部分是对所有架构的完整处理一个人花了太多时间在依赖注入标签周围徘徊的问题 .

    • 阅读Mark Seemann's top rated posts here on SO right now - 您将从每个人那里学到有 Value 的技巧

相关问题