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 with 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 されて見過ごされているコメントの他のいくつかの点を明確にする必要があります。Ninject であれ、その他の目的であれ、DI を使用する際に考慮すべきいくつかの重要事項:

  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. statics と singleton の使用を最小限に抑える

  4. [global] コンテナが 1 つしかなく、適切なグローバル変数のように必要なときにいつでも要求できると想定しないでください。複数のモジュールを正しく使用すると、Bind.ToProvider()これを管理するための構造が得られます。そうすれば、それぞれの個別のサブシステムが独自に機能し、下位レベルのコンポーネントが上位レベルのコンポーネントなどに関連付けられることはありません。

誰かが私が参照しているブログへのリンクを記入したい場合は、それをいただければ幸いです (ただし、それらはすべて SO の他の投稿から既にリンクされているため、これはすべて複製 UI が目的で導入したものです)誤解を招く回答による混乱を避けるためです。)

さて、Joel が入ってきて、何がいい構文なのか、および/またはこれを行う正しい方法を本当に教えてくれたらいいのに!

更新:この回答は、獲得した賛成票の数から明らかに有用ですが、次の推奨事項を作成したいと思います。

  • 上記は少し古くなっているように感じます。正直に言うと、.net で Dependency Injection を読んでから恥ずかしく感じる多くの不完全な考え方が反映されています - 今すぐ実行して購入してください - DI だけではなく、前半は完全な処理ですそれを取り巻くすべてのアーキテクチャの問題は、ここで依存性注入タグをぶらぶらしすぎた男からのものです。
  • SO で Mark Seemann の高評価の投稿を今すぐ読んでください。すべての投稿から貴重なテクニックを学ぶことができます。
于 2010-02-09T12:19:45.787 に答える