web-dev-qa-db-ja.com

C#/ Unityでのコンストラクタインジェクション?

MicrosoftのUnityフレームワークでC#を使用しています。この問題を解決する方法がよくわかりません。それはおそらく、UnityでのDIの理解の欠如と関係があります。

私の問題は、次のサンプルコードを使用して要約できます。

class Train(Person p) { ... }

class Bus(Person p) { ... }

class Person(string name) { ... }

Person dad = new Person("joe");
Person son = new Person("timmy");

バスでresolveメソッドを呼び出すと、「timmy」という名前の人「son」が挿入されていることを確認できます。また、Trainを解決するときに、「joe」という名前の人「dad」が解決されていることを確認するにはどうすればよいですか。

名前付きインスタンスを使用することを考えていますか?しかし、私は途方に暮れています。どんな助けでもいただければ幸いです。

余談ですが、私はIPersonインターフェイスを作成したくありません。

16
JP Richardson

これを解決する1つの方法は、名前付き登録でインジェクションコンストラクターを使用することです。

// Register timmy this way  
Person son = new Person("Timmy");  
container.RegisterInstance<Person>("son", son);  

// OR register timmy this way  
container.RegisterType<Person>("son", new InjectionConstructor("Timmy"));  

// Either way, register bus this way.  
container.RegisterType<Bus>(new InjectionConstructor(container.Resolve<Person>("son")));  

// Repeat for Joe / Train
19
Daniel Auger

名前付き依存関係としてそれぞれ「joe」と「timmy」を登録しない限り、「timmy」がスクールバスに注入されているかどうかを確認することはできません。実際、同じクラスの2つのインスタンスを名前のない依存関係として登録しようとすると、セットアップがあいまいになり、Personをまったく解決できなくなります。

一般に、多くの名前付きインスタンスを登録する必要がある場合は、おそらく間違った方法でDIを実行しています。 DIの主なアイデアは、ドメインサービスよりもドメインオブジェクトを解決することです。

DIの主なアイデアは、抽象型(インターフェースまたは抽象クラス)具象型にを解決できるメカニズムを提供することです。あなたの例には抽象型がないので、あまり意味がありません。

35
Mark Seemann

マーク・シーマンはそれを正しく理解しました。そして、私はあなたの混乱に同情します。自動依存性注入コンテナの使い方を学んだとき、私は自分でそれを経験しました。問題は、オブジェクトを設計および使用するための有効で合理的な方法がたくさんあることです。しかし、これらのアプローチの一部のみが自動依存インジェクションコンテナで機能します。

私の個人的な歴史:UnityやCastleWindsorコンテナーなどの制御の反転コンテナーの使用方法を学ぶずっと前にOOオブジェクト構築と制御の反転の原則を学びました。コードを書く習慣を身に付けました。このような:

public class Foo
{
   IService _service;
   int _accountNumber;

   public Foo(IService service, int accountNumber)
   {
      _service = service;
      _accountNumber = accountNumber;
   }
   public void SaveAccount()
   {
       _service.Save(_accountNumber);

   }
}
public class Program
{
     public static void Main()
     {
        Foo foo = new Foo(new Service(),1234);
        foo.Save();
     }
}

この設計では、私のFooクラスがアカウントをデータベースに保存する責任があります。それを行うにはアカウント番号と汚い仕事をするためのサービスが必要です。これは、上記で提供した具象クラスにいくぶん似ています。ここでは、各オブジェクトがコンストラクターでいくつかの一意の値を取ります。これは、独自のコードでオブジェクトをインスタンス化するときに正常に機能します。適切なタイミングで適切な値を渡すことができます。

しかし、自動依存性注入コンテナーについて知ったとき、私はもはやFooを手動でインスタンス化していないことに気付きました。コンテナーは、コンストラクター引数をインスタンス化します。これは、IServiceのようなサービスにとって非常に便利でした。しかし、整数や文字列などでは明らかにうまく機能しません。そのような場合、デフォルト値(整数のゼロなど)が提供されます。代わりに、アカウント番号や名前などのコンテキスト固有の値を渡すことに慣れていました。そのため、コーディングとデザインのスタイルを次のように調整する必要がありました。

public class Foo
{
   IService _service;
   public Foo(IService service)
   {
      _service = service;
   }
   public void SaveAccount(int accountNumber)
   {
       _service.Save(accountNumber);

   }
}
public class Program
{
     public static void Main()
     {
        Foo foo = new Foo(new Service());
        foo.Save(1234);
     }
}

どちらのFooクラスも有効な設計のようです。ただし、2つ目は自動依存性注入で使用でき、1つ目は使用できません。

14
David Allen