【Unitiy】RegisterTypeメソッドの引数にインジェクション情報を付加する。

  • 2009.03.01 Sunday
  • 19:32
Unity InjectionConstructor

Source and Project

IUnityContainerインターフェイスで公開されているRegisterTypeメソッドの引数は必ず、

params InjectionMember[] injectionMembers

をとるようになっています。これはおそらくUnity Application Block 1.1にはなく、Unity Application Block 1.2から追加されたRegisterTypeの仕様だと思いますが、どのバージョンからは正確には確認していません。InjectionMember抽象クラスを継承するクラスは下記のとおりです。

InjectionConstructorクラス
 コンストラクタインジェクションをサポート。
InjectionMethodクラス
 メソッドインジェクションをサポート。
InjectionPropertyクラス
 プロパティインジェクションをサポート。


使い方はいたって簡単です。ちょこっとサンプルコードを書くと

 class Program
 {
  static void Main(string[] args)
  {
   IUnityContainer container = new UnityContainer();
   container.RegisterType<IA, AImpl>("A", new InjectionConstructor(1));

   var a = container.Resolve<IA>("A");
   a.Execute();
  }
 }

 internal interface IA
 {
  void Execute();
 }
 
 class AImpl : IA
 {
  //[InjectionConstructor]
  public AImpl()
  {
   Console.WriteLine("AImpl()");
  }

  public AImpl(int dummy)
  {
   Console.WriteLine("AImpl(int dummy = " + dummy + " )");
  }

  public void Execute()
  {
   Console.WriteLine("AImpl.Execute()");
  }
 }

IAをコンテナからのアウトプット用のTypeとし、その設計図としてAImplクラスを静的な情報としてRegisterTypeメソッドを使用しコンテナに登録します。ただし、AImplのコンストラクタは2つ存在し、あいまいです。このあいまい性をなくするため二つのアプローチが用意されています。ひとつは属性ベースによるカレントのコンストラクタの指定です。属性ベースではクラス内に、どのコンストラクタを使用するかマークしていたわけです。今回はそれをしていません(わざとらしくコメントアウトしています)今回、使用しているのはコンテナ外部からの指定です。RegisterTypeメソッドを使用しています。

container.RegisterType<IA, AImpl>("A", new InjectionConstructor(1));

このメソッド呼び出しの第二引数がそれにあたります。コンストラクタのシグニチャと実際の値を指定することで、複数のコンストラクタのあいまい性を回避しています。属性ベースの場合、静的な情報となるため、複数のコンストラクタというのはあまり意味をなしえません。また、属性は複雑な指定ができないため、簡単にはインジェクションができないと思われます(実際に検証していないのでよくわりませんが・・・)逆に動的に実行されえるRegisterTypeメソッドの呼び出しは、コンストラクタインジェクションの汎用性を高め、複雑なコンストラクタをコンテナ上で行うことに対応できます。もちろんRegisterIntanceのようにコンストラクタ自身も外部で行えば、プログラマの思う通りのものが作れるわけですが、ちょっとだけ選択股が増えたということです。

今回はRegisterTypeメソッドで行うコンストラクタインジェクションに焦点をあててみましたが、やっぱりUnity Application Blockは結構ポテンシャルが高いと感じます。いや本当。面白い。あと、Unityのソースコードが読みやすいので勉強になります。

あーそうそう実行結果

Unity InjectionConstructor


Source and Project

【Unity】RegisterInstanceとRegisterTypeの違い。

  • 2009.03.01 Sunday
  • 02:51
RegisterInstance

Source and Project

Unity Application Block 1.2のRegisterInstanceメソッドとRegisterTypeメソッドの違いに関して取り上げます。

RegisterInstanceメソッド
RegisterInstanceメソッドはDIコンテナにTypeとNameをキー情報として、Typeにキャスト可能なインスタンスを登録することができます。LifetimeManagerを指定しない場合は、ContainerControlledLifetimeManagerがデフォルト値として設定されます。

RegisterInstanceメソッドで登録した場合に注意しなければならないのは、DIコンテナからインスタンスを破棄した場合です。

Typeがインターフェイスの場合
RegisterInstanceメソッドは外部で生成したインスタンスを登録しているため、基本的にはインスタンスの生成方法をコンテナは知りません。何らかの方法でコンストラクタする方法をコンテナに知らせて置かないと、一度、インスタンスを破棄した後にインスタンスをコンテナから取得しようとすると例外(Microsoft.Practices.Unity.ResolutionFailedException)が発生します。LifetimeManagerがコンテナ単位でシングルトンになるようにContainerControlledLifetimeManagerが設定されているのは、上記のような理由があるからと推測されます。

Typeがクラスの場合
指定されたクラスにカレントのコンストラクタ情報を指定している場合のみコンテナの格納されているインスタンスを破棄しても新しいインスタンスが生成可能です。ただし、そのような使用ケースは本来RegisterTypeメソッドで行うべきです。

RegisterTypeメソッド
RegisterTypeメソッドは以前にも紹介しましたが、RegisterTypeメソッドはコンテナにTypeおよびNameをキー情報として、クラス(静的な情報)を関連づけます。どのコンストラクタをカレントとするか指定し、必要な引数をインジェクションする設定をしていれば、ResolveまたはResolveAllなどを実行した際に、指定したクラスからインスタンスが作成できます。


RegisterTypeとResigsterInstanceの違いは、静的な情報を登録するか動的な情報を登録するかの違いです。また、別の言い方をすれば、コンテナ内でインスタンスを作成する(実際には作成するための情報)か、外部で作成したインスタンスをコンテナに渡すかの違いです。ResigsterTypeはクラス同士の関連とオブジェクト同士の関連に差異がないようなケースで有効です。この場合、静的な情報がそのまま動的な情報になるからです。つまりコンパイル時に宣言した内容がそのままオブジェクトとして動作可能な状況です。ResigsterInstanceの場合は、DIコンテナの機能ではサポートできないような複雑なコンストラクタや別のインスタンス作成機構がある場合に有効です。また、ユーザのアクションによって、コンテナで管理する必要が発生するケースにも対応できます。ということで、状況に応じて、
RegisterTypeとResigsterInstanceは使い分けることができます。また、キー情報となるTypeとNameは共有されているので、後から書き換える事もできます。

今回のサンプルコードはRegisterInstanceメソッドの簡単なサンプルコードです。

 class Program
 {
  static void Main(string[] args)
  {
   IUnityContainer container = new UnityContainer();
   var lifetimeManager = new ContainerControlledLifetimeManager();
   container.RegisterInstance<IA>("A", new AImpl(), lifetimeManager);

   var a = container.Resolve<IA>("A");
   a.Execute();

   lifetimeManager.Dispose();

   try
   {
    var a2 = container.Resolve<IA>("A");
   }
   catch (ResolutionFailedException e)
   {
    Console.WriteLine(e.Message);
   }
  }
 }

 internal interface IA
 {
  void Execute();
 }
 
 class AImpl : IA
 {
  public AImpl()
  {
   Console.WriteLine("AImple()");
  }

  public void Execute()
  {
   Console.WriteLine("AImpl.Execute()");
  }
 }

実行すると

RegisterInstance

まとめます。
1.RegisterTypeメソッドで静的な情報をType(OutputのType)とNameをキーにして
  登録することができる。
2.RegisterInstanceメソッドで動的な情報をType(OutputのType)とNameをキーにして
  登録することができる。

以上!

Source and Project

【Unity】【DIコンテナ】セッターインジェクションとコンストラクタインジェクション

  • 2009.02.26 Thursday
  • 22:55
そろそろUnityもセッターインジェクションやらコンストラクタインジェクションの話に入りそうです。その前にTypeとNameの関係を間違って紹介してしまったので修正しないといけませんがー。

今回は設計に近い実装の話。
シングルトンを前提としてDIコンテナ上でロジックオブジェクトを生成する場合、コンストラクタインジェクションを用いる方が望ましいといわれています。南無南無。でも、実際にコーディングしてみると、プログラマにコーディング規約などでちょっとした縛りを設ければ、セッターインジェクションの方がはるかにコーディングがしやすくなります。これは単なる実感で、確かに「望ましい」けどセッターインジェクションの方がいいなぁーと思えます。

たとえば、下記のようにP1プロパティにセッターインジェクションを用いる場合はセッターとなるプロパティのsetterはDIコンテナから見えるようにpublicで公開する必要があります。

public class A : IA
{
 public IB P1 { private get; set; }
 public void Method() {...}
}

さて、DIコンテナが生成したAオブジェクトはシングルトンなのでしょうか?私はシングルトンの定義をはっきりと明言するスキルはありませんが、複数のユーザが使用するシングルトンで、かつ、ユーザがいつでもシングルトンを使用できる場合は、状態を持たせてはいけないという事は、はっきりといえます。しかし、セッターインジェクションの場合、コンストラクタの後にDIコンテナがセッターインジェクションを実行するまではP1はnullなわけですから思いっきり変化するフィールド、状態を持っていることになります。また、DIコンテナでなくてもAオブジェクトはP1プロパティのセッターを公開しているわけですから、第三者が意図的に値を変更できます。前者はおそらくDIコンテナがうまくかわしてくれると思いますが、後者はプログラマのアウチなミスで人為的に起こる可能性があります。

では、理想的に書くにはどうするか!?

public class A : IA
{
 private readonly IB p1;
 public A(IB p1)
 {
  this.p1 = p1;
 }
 public void Method() {...}
}

これでp1の値を買い替えられえる心配もなく、このオブジェクトのコンストラクタが終われば必ずp1は不変な値を持つことになります。これは言語的な縛りでさわやかシングルトンを提供しているので、使う側も安心、安心です。ただし、コンストラクタインジェクションでフィールドの値を設定するのは結構面倒です。さらに、セッターインジェクションより間違えやすいです。セッタープロパティを外部で書き換えるのと、設定ファイルや属性の書き間違え、どっちが起こりやすく、どっちが発見しにくいか・・・統計的な情報は何ひとつありませんが、経験則から言うとプログラマはコードを書くのがお仕事ですから、自分が書いたコードに間違いがあるときすぐに発見できます。そもそも、DIコンテナ用にプロパティは書き換えないというルールで縛られているため、書き換える動機を与えません。逆に、設定というは結構間違いやすいですよね。よね?よね?なので、理想と現実のトレードオフにかければおのずと結論がでるわけです。

こんな感じでセッターインジェクションが好まれるわけです。南無南無。

よし、これで説明すればうまくいけそうだ!

【Unity】RegisterType : 宣言から見るメソッドの責務。

  • 2009.02.21 Saturday
  • 11:20
前回(【Unity】RegisterType : 静的な情報を登録する。)は、DIコンテナが管理する「継承関係(is - a)※自分自身もありうる」の管理方法を考察してみました。今回もRegisterTypeメソッドのシグネチャから、前回の裏付けを行いたいと思います。

IUnityContainerインターフェイスにRegisterTypeメソッドが宣言されており、IUnityContainerインターフェイスを継承するクラスがRegisterメソッドを実装していることになります。実際にIUnityContainerインターフェイスからRegisterメソッドを抜き出してみると、さまざまなオーバーロードが存在することがわかります。

public interface IUnityContainer : IDisposable
{
 IUnityContainer RegisterType<T>(params InjectionMember[] injectionMembers);
 IUnityContainer RegisterType<TFrom, TTo>(params InjectionMember[] injectionMembers) where TTo : TFrom;
 IUnityContainer RegisterType<TFrom, TTo>(LifetimeManager lifetimeManager, params InjectionMember[] injectionMembers) where TTo : TFrom;
 IUnityContainer RegisterType<TFrom, TTo>(string name, params InjectionMember[] injectionMembers) where TTo : TFrom;
 IUnityContainer RegisterType<TFrom, TTo>(string name, LifetimeManager lifetimeManager, params InjectionMember[] injectionMembers) where TTo : TFrom;
 IUnityContainer RegisterType<T>(LifetimeManager lifetimeManager, params InjectionMember[] injectionMembers);
 IUnityContainer RegisterType<T>(string name, params InjectionMember[] injectionMembers);
 IUnityContainer RegisterType<T>(string name, LifetimeManager lifetimeManager, params InjectionMember[] injectionMembers);
 IUnityContainer RegisterType(Type t, params InjectionMember[] injectionMembers);
 IUnityContainer RegisterType(Type from, Type to, params InjectionMember[] injectionMembers);
 IUnityContainer RegisterType(Type from, Type to, string name, params InjectionMember[] injectionMembers);
 IUnityContainer RegisterType(Type from, Type to, LifetimeManager lifetimeManager, params InjectionMember[] injectionMembers);
 IUnityContainer RegisterType(Type t, LifetimeManager lifetimeManager, params InjectionMember[] injectionMembers);
 IUnityContainer RegisterType(Type t, string name, params InjectionMember[] injectionMembers);
 IUnityContainer RegisterType(Type t, string name, LifetimeManager lifetimeManager, params InjectionMember[] injectionMembers);
 IUnityContainer RegisterType(Type from, Type to, string name, LifetimeManager lifetimeManager, params InjectionMember[] injectionMembers);
 
 ...
}

全部で16個のメソッドが存在します。しかし、実際には半分はジェネリックの拡張なので実行時の多様性としては8個のタイプがあるといえます。RegisterTypeにジェネリックの拡張が存在する理由として、コンパイル時の型のチェックが可能になる利便性が挙げられます。

たとえば継承関係にないクラスAとクラスBがあったとします。これに対してRegisterTypeメソッドを実行し、DIコンテナに設定情報として登録しようした場合に

 class Program
 {
  static void Main()
  {
   IUnityContainer container = new UnityContainer();
   container.RegisterType(typeof(A), typeof(B));
  }
 }

 public class A { }
 public class B { }


上記のような非ジェネリックなRegisterTypeを呼び出した際は実行時エラーとなりバグの発見が遅れ、かつ見つけにくバクとなってしまいます。ちなみにこの実行時エラーは下記のメソッドのチェックに引っ掛かりUnityから例外が投げられます。

Type.IsAssignableFrom メソッド
指定した Type のインスタンスを現在の Type のインスタンスに代入できるかどうかを判断します。
http://msdn.microsoft.com/ja-jp/library/system.type.isassignablefrom(VS.80).aspx


この非ジェネリックの生産性を下げる部分を補完するためにジェネリックなメソッドが用意されています。

 class Program
 {
  static void Main()
  {
   IUnityContainer container = new UnityContainer();
   container.RegisterType<A, B>();
  }
 }

 public class A { }
 public class B { }


この場合、コードをコンパイルするとコンパイルエラーになるのですぐに間違いに気づくことができます。しかもタイプセーフが実現(Resolveメソッドなどでは顕著です。)できます。ちなみにReSharperというツールを入れると、コンパイル前のコーディング中に間違いを指摘してくれるのでさらに便利です。お試し程度しかコードを書かない人には意味のないツールですが、コードをがりがり書く人にとっては、おそらく誰もが歓迎してくれるツールだとおもいます(推奨メモリが4Gです)

とまあ、Registerメソッドの責務とはあまり関係のない部分にふれてしまいましたが、これからRegisterメソッドの責務の関係のある部分に触れてみます。

このジェネリックのコンパイルエラーを出すための制約部分を見てみます。

IUnityContainer RegisterType<TFrom, TTo>(params InjectionMember[] injectionMembers) where TTo : TFrom;

これが無名の登録を行うRegisterメソッドの宣言です。where TTo : TFrom が制約となっています。この制約はType.IsAssignableFrom メソッドとほぼ同等ですので、is - a な関係で表現するとTFrom is a TToと解釈できます。ということで、RegisterTypeがis - aな関係を登録しているということが宣言からも理解できます。今まで全く触れていませんが、InjectionMemberがparamsでとるようになっていますが、これはコンストラクタインジェクション、セッターインジェクション、メソッドインジェクションなどをサポートする仕組みです。

とりあえず上記の説明で

 IUnityContainer RegisterType<TFrom, TTo>(params InjectionMember[] injectionMembers) where TTo : TFrom;
 IUnityContainer RegisterType(Type from, Type to, params InjectionMember[] injectionMembers);

は満たしたことになりますが、これの拡張版が

 IUnityContainer RegisterType<TFrom, TTo>(LifetimeManager lifetimeManager, params InjectionMember[] injectionMembers) where TTo : TFrom;
 IUnityContainer RegisterType(Type from, Type to, LifetimeManager lifetimeManager, params InjectionMember[] injectionMembers);

 IUnityContainer RegisterType<TFrom, TTo>(string name, params InjectionMember[] injectionMembers) where TTo : TFrom;
 IUnityContainer RegisterType(Type from, Type to, string name, params InjectionMember[] injectionMembers);

 IUnityContainer RegisterType<TFrom, TTo>(string name, LifetimeManager lifetimeManager, params InjectionMember[] injectionMembers) where TTo : TFrom;
 IUnityContainer RegisterType(Type from, Type to, string name, LifetimeManager lifetimeManager, params InjectionMember[] injectionMembers);

です。LifetimeManagerを指定したり、名前を指定したりすることが可能なオーバーロードです。

これで半分のメソッドを満たしました。あとはこれとは別なタイプのメソッドです。それはis - a な関係が自分自身の場合です。 Class Aというクラスが存在した場合に A is a Aということになります。なのでTFromとTToの二つのタイプをとる必要がなくタイプの引数はTのみになります。

ということで

 IUnityContainer RegisterType<T>(params InjectionMember[] injectionMembers);
 IUnityContainer RegisterType<T>(LifetimeManager lifetimeManager, params InjectionMember[] injectionMembers);
 IUnityContainer RegisterType<T>(string name, params InjectionMember[] injectionMembers);
 IUnityContainer RegisterType<T>(string name, LifetimeManager lifetimeManager, params InjectionMember[] injectionMembers);

 IUnityContainer RegisterType(Type t, params InjectionMember[] injectionMembers);
 IUnityContainer RegisterType(Type t, string name, params InjectionMember[] injectionMembers);
 IUnityContainer RegisterType(Type t, string name, LifetimeManager lifetimeManager, params InjectionMember[] injectionMembers);
 IUnityContainer RegisterType(Type t, LifetimeManager lifetimeManager, params InjectionMember[] injectionMembers);

がすべて簡単に説明がつくオーバーロードとなります。たまにオーバーロードのくせに中で行っている事が全く違うへんちくりんな実装があったりすると、(自分が書いてない場合のみ)発狂しそうになりますが(自分が書いたらコッソリ直します)このResigterTypeは素直なオーバーロードで安心です。

おそらくは

 IUnityContainer RegisterType(Type from, Type to, string name, LifetimeManager lifetimeManager, params InjectionMember[] injectionMembers);

上記のメソッドが一個あれば、アプリケーション的には問題ないコードが書けるはずですが、さすがに毎回指定するのは面倒です。

結局ひとつあればよいというところまで分かりましたので、このメソッドを見てみると

静的な情報としてTypeが二つ、その関係の名前、fromがインスタンス化された時のライフライムを管理するためのlifetimeManager、そして、fromをインスタンス化する際に必要なインジェクション情報の配列(injectionMembers)をRegisterメソッドを通じてDIコンテナに登録していることがわかります。

以上

【Unity】RegisterType : 静的な情報を登録する。

  • 2009.02.21 Saturday
  • 06:40
TODO: 全体的に間違っているので修正する予定

--------------------------------------------------------------

Unity RegisterType

Source and Project

DIコンテナというフレームワークが提供する機能は

・静的な情報と動的な情報を管理する機能
・静的な情報から動的な情報を構築する機能


と考えることができます。

Unity Application Blockが提供するResigterTypeメソッドは静的な情報をDIコンテナに登録する機能を提供します。似たメソッドとしてResigterInstanceメソッドというのもあり、こちらは静的な情報を設定することも可能ですが、動的な情報を直接DIコンテナに登録することもでき少し特殊です。

今回はResigterTypeメソッドに関して少しまとめてみたいと思います。

-----------------------------------------------------
RegisterTypeメソッドから見る継承関係の登録と管理概念。

RegisterTypeをブラックボックスおよびホワイトボックスの双方から検証し
依存関係の登録方法と登録された依存関係の管理概念を考察してみました。

DIコンテナに登録される静的な情報を列挙してみると

1.継承関係(is-a)(※自分自身もありうる。)
2.関連関係(has-a)または依存関係(use)
3.その他(1と2から生成されたインスタンスの管理方法など)


Unity Application Blockではこれらの情報の設定をXMLファイルとコードのどちらかでも設定可能で、かつ設定情報自体をオブジェクト化しているため動的に変更が可能であることが特徴です。今回とりあげるRegisterTypeメソッドの役割はis - a の関係を登録する部分(1)と、インジェクションの設定(2)、それからインスタンス化されたオブジェクトの管理方法の登録を行う責務(3)を担っています。Unityでは、(1)(2)(3)の設定を名前で管理します。この名前はユニークであり、動的変化に対応するため、重複する名前は後勝ちになります。Unityでは名前付けの特殊なケースとして無名が存在します。無名に対しても普通の名前と同様にis - aの関係を1つ対応しており他の名前と同等な重みで扱われ、無名と無名でない名前に対しての優先順位は存在しません。つまり、該当する名前がなかったから無名な関係を結果として返すなどといった仕組みは存在しません。

たとえば、

名前 A - 関係 (HogeImpl is a IHoge)
名前 B - 関係 (HogeImpl is a IHoge)
名前 C - 関係 (HogeAImpl is a IHoge)
名前 無名 - 関係 (HogeAImpl is a IHoge)

下記のより無名は無名ととして他のA、B、Cと同列で扱われているので
Aを検索すると IHoge - HogeImpl が名前指定なしで検索すると、無名という名前で検索されIHoge - HogeAImplがという具合に考えることができます。

コード中に無名そのものの対してNullオブジェクトは存在しませんが、
結果的にはNullオブジェクト的な振る舞いをしているといえると思います。

よって下記のコードでは

 class Program
 {
  static void Main()
  {
   IUnityContainer container = new UnityContainer();
   container.RegisterType<IHoge, HogeAImpl>("A");
   container.RegisterType<IHoge, HogeBImpl>();
   container.RegisterType<IHoge, HogeCImpl>();

   container.Resolve<IHoge>().Execute();
   container.Resolve<IHoge>("A").Execute();
  }
 }

 public interface IHoge
 {
  void Execute();
 }

 public class HogeAImpl : IHoge
 {
  public void Execute()
  {
   Console.WriteLine("A");
  }
 }

 public class HogeBImpl : IHoge
 {
  public void Execute()
  {
   Console.WriteLine("B");
  }
 }

 public class HogeCImpl : IHoge
 {
  public void Execute()
  {
   Console.WriteLine("C");
  }
 }

Unity RegisterType

無名で登録が2行あります。

   container.RegisterType<IHoge, HogeBImpl>();
   container.RegisterType<IHoge, HogeCImpl>();

これは無名という名前で登録することになり、最初の関係は
後の登録によって上書きされてしまいます。

よって

container.Resolve<IHoge>().Execute();

無名での呼び出しはHogeCImplが呼び出されることになります。

また下記のように無名の登録をしないまま

  static void Main()
  {
   IUnityContainer container = new UnityContainer();
   container.RegisterType<IHoge, HogeAImpl>("A");
   //container.RegisterType<IHoge, HogeBImpl>();
   //container.RegisterType<IHoge, HogeCImpl>();

   container.Resolve<IHoge>().Execute();
   container.Resolve<IHoge>("A").Execute();
  }

無名で呼び出すと実行時に例外が発生します。

例外メッセージの一部
Resolution of the dependency failed, type = "Art55.UnityDemo20090218_001.IHoge", name = ""

無名で登録されている依存関係がみつかりませーんって言っていることがわかります。

ではまとめます。

DIコンテナの継承関係(is - a)の登録は名前で管理される。
ただし、名前指定なしの場合は無名としてひとつの設定となる。


以上

Source and Project

------------------
誤解を招く表現があったため、編集しました。

【Unity】LifetimeManagerのまとめ。

  • 2009.02.20 Friday
  • 01:52
Unity LifetimeManager

Source and Project

UnityのLifetimeManagerオブジェクトのメソッドをそれぞれまとめてみました。

TransientLifetimeManager
オブジェクトの管理方法
 管理しない。
GetValueメソッド
 常にnullを返す。
SetValueメソッド
 特に何もしない。
RemoveValueメソッド
 特に何もしない。
備考
 LifetimeManager指定なしのRegisterTypeメソッドとRegisterInstanceは
 このLifetimeManagerが指定される。


PerThreadLifetimeManager
オブジェクトの管理方法
 スレッド毎にオブジェクトを管理する(強い参照)
GetValueメソッド
 コンテナに関連付けられているスレッドとペアになっているオブジェクトを返す。
SetValueメソッド
 関連付けられているスレッド毎にオブジェクトを保持する。
RemoveValueメソッド
 特に何もしない。


ExternallyControlledLifetimeManager
オブジェクトの管理方法
 オブジェクトを弱い参照により管理する。
GetValueメソッド
 管理するオブジェクトの強い参照を返す。ただしGC回収後はnullを返す。
SetValueメソッド
 指定したオブジェクトの弱い参照で管理対象として保持する。
RemoveValueメソッド
 特に何もしない。


ContainerControlledLifetimeManager
オブジェクトの管理方法
 コンテナ毎にオブジェクトを管理する(強い参照)
GetValueメソッド
 管理するオブジェクトを返す。
SetValueメソッド
 指定したオブジェクトを管理対象として保持する。
RemoveValueメソッド
 管理するオブジェクトがIDisposableを継承している場合、Disposeメソッドを呼び出す。
Disposeメソッド
 RemoveValueメソッドと同じ


これらの全部試してみるとは、面・・・諸事情により割愛されていただきます。ContainerControlledLifetimeManagerだけちょろんと試してみました。そろそろ、この試してみましたモードは飽きてきました(笑)

 class Program
 {
  static void Main()
  {
   IUnityContainer container = new UnityContainer();
   var lifetimeManager = new ContainerControlledLifetimeManager();
   container.RegisterType<IHoge, HogeAImpl>(lifetimeManager);

   // コンテナから取り出したオブジェクトとlifetimeManagerから取り出したオブジェクトの比較。
   IHoge hoge = container.Resolve<IHoge>();
   Console.WriteLine(hoge.GetHashCode() == lifetimeManager.GetValue().GetHashCode());

   // SetValueで管理するインスタンスを入れ替える。
   IHoge hoge2 = new HogeAImpl();
   lifetimeManager.SetValue(hoge2);
   Console.WriteLine(hoge.GetHashCode() == lifetimeManager.GetValue().GetHashCode());
   Console.WriteLine(lifetimeManager.GetValue().GetHashCode() == container.Resolve<IHoge>().GetHashCode());

   // 管理するインスタンスをいったん破棄する。
   lifetimeManager.RemoveValue();
   Console.WriteLine(hoge2.GetHashCode() == container.Resolve<IHoge>().GetHashCode());
  }
 }

 public interface IHoge
 {
  void Execute();
 }

 public class HogeAImpl : IHoge
 {
  public void Execute()
  {
   Console.WriteLine("Hey");
  }
 }

はい。実行!

Unity LifetimeManager

lifetimeManagerからGetValueメソッドで管理中のオブジェクトを取り出したり、SetValueメソッドで入れ替えたりとなんだかとっても奇妙な感じですが(笑)うごいてますねー。あたりまえですがー。

Source and Project

【Unity】PerThreadLifetimeManagerの振る舞い。

  • 2009.02.18 Wednesday
  • 21:13
PerThreadLifetimeManager

Source and Project

PerThreadLifetimeManagerの振る舞いを調べてみました。まずはクラス図を見てみました。

PerThreadLifetimeManager

グローバル一意識別子 (GUID) 毎にオブジェクト管理するようになっていることが読み取れます。また、PerThreadLifetimeManagerが生成されたグローバル一意識別子 (GUID) も保持していることがわかります。

このLiftimeManangerに関しては疑問があるのですが、それは登録された依存関係はスレッド間で共有さるのかどうかということです。また、登録されているLifttimeManager自身のライフライムも気になります。

ということでコードで検証してみました。

 class Program
 {
  static void Main()
  {
   IUnityContainer container = new UnityContainer();
   var lifetimeManager = new PerThreadLifetimeManager();
   container.RegisterType<IHoge, HogeAImpl>("A", lifetimeManager);

   var hoge1 = container.Resolve<IHoge>("A");
   var hoge2 = container.Resolve<IHoge>("A");
   IHoge hoge3 = null;
   IHoge hogeB1b = null;

   new Thread(() =>
   {
    var lifetimeManagerB = new PerThreadLifetimeManager();
    container.RegisterType<IHoge, HogeAImpl>("B", lifetimeManagerB);
    hoge3 = container.Resolve<IHoge>("A");
    hogeB1b = container.Resolve<IHoge>("B");

    lifetimeManagerB = null;
    GC.Collect();
    Console.WriteLine("GC.Collet()実行。");
   }).Start();
   Thread.Sleep(1000);

   Console.WriteLine(ReferenceEquals(hoge1, hoge2));
   Console.WriteLine(ReferenceEquals(hoge1, hoge3));
   
   var hogeB2 = container.Resolve<IHoge>("B");
   Console.WriteLine(hogeB2 != null ? "別スレッドで登録したBを呼び出せた。" : "別スレッドで登録したBを呼び出せなかった。");
   Console.WriteLine(ReferenceEquals(hogeB1b, container.Resolve<IHoge>("B")));

   int hashHoge = hoge1.GetHashCode();

   hoge1 = hoge2 = hoge3 = null;

   GC.Collect();
   Console.WriteLine("GC.Collet()実行。2");

   var hoge4 = container.Resolve<IHoge>("A");

   Console.WriteLine(hashHoge == hoge4.GetHashCode());
  }
 }

 public interface IHoge
 {
  void Execute();
 }

 public class HogeAImpl : IHoge
 {
  public void Execute()
  {
   Console.WriteLine("Hey");
  }
 }

これだともう、きちんとテストケースを分けた単体テストを行うべきだと思うようになってきましたが・・・それも面・・・いや、時間の都合上ちょろんと書いてみました。

実行結果は

PerThreadLifetimeManager

ということで、PerThreadLifetimeManagerは

コンテナに登録した依存関係は別スレッドからでも、Resolve可能である。ただし、スレッド毎に別インスタンスが生成されスレッド毎に共有される。

ということがわかりました。

Webサービスのようなセッション単位で状態を保持したい場合などに最適

と思います。ただし、

共有したいステートレスなロジックに対してはContainerControlledLifetimeManagerを使用して、なるべくメモリに負荷をかけない方が良い。

と思います。

Source and Project

【Unity】ExternallyControlledLifetimeManagerの振る舞い。

  • 2009.02.18 Wednesday
  • 20:49
ExternallyControlledLifetimeManager

Source and Project

今回はライフマネージャーの一つであるExternallyControlledLifetimeManagerを見てみたいと思います。まずはクラス図を見てみると

ExternallyControlledLifetimeManager

という風になります。特徴的なのはWeakRefrenceを包含していることです。このWeakRefrenceオブジェクトはDIコンテナから生成したオブジェクトへの「弱い参照」を実現するオブジェクトだと想像できます。弱い参照以外の参照がすべてなくなりGCに回収された後にDIコンテナから、同じ型、または同じ名前のResolveが呼び出された場合は新しいインスタンスを返すはずです。

では実際に試してみます。

 class Program
 {
  static void Main()
  {
   IUnityContainer container = new UnityContainer();
   var lifetimeManager = new ExternallyControlledLifetimeManager();
   container.RegisterType<IHoge, HogeAImpl>("A", lifetimeManager);

   IHoge hoge1 = container.Resolve<IHoge>("A");
   IHoge hoge2 = container.Resolve<IHoge>("A");
   IHoge hoge3 = null;
   new Thread(() =>
   {
    hoge3 = container.Resolve<IHoge>("A");
   }).Start();
   Thread.Sleep(1000);

   Console.WriteLine(ReferenceEquals(hoge1, hoge2));
   Console.WriteLine(ReferenceEquals(hoge1, hoge3));

   int hashHoge = hoge1.GetHashCode();

   hoge1 = hoge2 = hoge3 = null;

   GC.Collect();

   IHoge hoge4 = container.Resolve<IHoge>("A");

   Console.WriteLine(hashHoge == hoge4.GetHashCode());
  }
 }

 public interface IHoge
 {
  void Execute();
 }

 public class HogeAImpl : IHoge
 {
  public void Execute()
  {
   Console.WriteLine("Hey");
  }
 }

hoge1とhoge2は同一スレッドで名前"A"から生成されたオブジェクトです。hoge3は別スレッドで名前"A"から生成されたオブジェクトです。オブジェクトのhoge1, hoge2, hoge3の生成後に同一のチェックを実施、その後、hoge1, hoge2, hoge3の参照を外しGC.Collectで強制的に回収します。その後、hoge4をhoge1と同一スレッドで名前"A"からオブジェクトを要求します。事前にとっておいたhoge1のハッシュコードとhoge4のハッシュコードを比較し、同一かどうかチェックします。

これが一連の流れですが、実際に実行してみると

ExternallyControlledLifetimeManager

GC.Collect前に取得したインスタンスはすべて同一。
GC.Collect前後で取得したインスタンスは同一でない。

という結果が得られました。

つまり、

Rsolveで取得したインスタンスがGCで回収されない限り、Rsolveは同一インスタンスを返し、GCで回収された場合は新しいインスタンスが生成される。

ということですね。

ステートレスなクラスで、インスタンス生成に比較的負荷がかからないようなクラスに対しては、このExternallyControlledLifetimeManagerが最適。

と思います。

以上

Source and Project

【WPF】【Unity】ContainerControlledLifetimeManager

  • 2009.02.17 Tuesday
  • 23:12
ContainerControlledLifetimeManager

Source and Project

【WPF】【Unity】コンテナからのインスタンスの提供方法を決定するオブジェクト。の続きです。DIコンテナで管理するインスタンスのライフタイムをマネージするオブジェクトの一つであるContainerControlledLifetimeManagerに関して調べてみました。

コードどーんです。

 class Program
 {
  static void Main()
  {
   IUnityContainer container = new UnityContainer();
   var lifetimeManager = new ContainerControlledLifetimeManager();
   container.RegisterType<IHoge, HogeAImpl>("A", lifetimeManager);

   IHoge hoge1 = container.Resolve<IHoge>("A");
   IHoge hoge2 = container.Resolve<IHoge>("A");
   IHoge hoge3 = null;
   new Thread(() =>
   {
    hoge3 = container.Resolve<IHoge>("A");
   }).Start();
   Thread.Sleep(1000);

   lifetimeManager.Dispose();

   IHoge hoge4 = container.Resolve<IHoge>("A");

   Console.WriteLine(ReferenceEquals(hoge1, hoge2));
   Console.WriteLine(ReferenceEquals(hoge1, hoge3));
   Console.WriteLine(ReferenceEquals(hoge1, hoge4));
  }
 }

 public interface IHoge
 {
  void Execute();
 }

 public class HogeAImpl : IHoge
 {
  public void Execute()
  {
   Console.WriteLine("Hey");
  }
 }

今回のDIコンテナへの登録方法は名前付けしています。おそらくこれは同一のターゲットの型を持つ複数の実装クラスが存在する場合などに名前を付けて生成するオブジェクトを特定する仕組みなどにりようされるものだと思いますが、とりあえず使ってみました(オイオイ)

今回のライフタイムはContainerControlledLifetimeManagerです。

生成されたインスタンスは変数にhoge1、hoge2、hoge3、hoge4に格納します。ここに見ていくと

hoge1は最初にDIコンテナから生成したHogeAImplオブジェクトです。
hoge2はhoge1と同一スレッドのDIコンテナから取得したHogeAImplオブジェクトです。
hoge3は別スレッドのDIコンテナから取得したHogeAImplオブジェクトです。
それからしばらく時間をおいて(別スレッドで実行されたメソッドがおそらく完了している)
ライフライムマネージャからDisposeメソッド呼び出し、このマネージャに管理されているDIコンテナ中のインスタンスがDIコンテナから破棄されます。
hoge4は上記処理の後にDIコンテナから呼び出したHogeAImplオブジェクトです。

よって、このアプリケーションの実行結果は
hoge1とhoge2は同一
hoge1とhoge3は同一
hoge1とhoge4は同一ではない。
と予測されます。

実際に結果をみると

ContainerControlledLifetimeManager

ということで予測は正しいようです。N-tierモデルをDIコンテナで実装する場合はこのContainerControlledLifetimeManagerで実装するのが最適かとおもいます。それ以外で実装するのはインスタンスの管理が難しいと思います。簡単シンプルシングルトンで、DIコンテナで管理されているクラスがステートレスになっていることをに目を光らせておけば、DIコンテナで実現するN-tierモデルのアプリケーションはテストもすごく楽になるはずです。

まとめます。

1.ContainerControlledLifetimeManagerはシングルトン。

Source and Project

【WPF】【Unity】コンテナからのインスタンスの提供方法を決定するオブジェクト。

  • 2009.02.17 Tuesday
  • 21:42
先日からUnity Application Blockを取り上げてBlogに書いております。そもそもUnity Application Blockを試してみたいと思ったのは3度目でして、一発目は6月か7月あたり、これは人づてに噂を聞いたからで、それまでDIコンテナはSeasar.NETしか触ったことがなかったので興味をしてしていたのですが、WPFの触っている方が楽しかったので、煙もただずな状態でした、それから「Composite Application Library」に興味を持った去年の10月ごろ、ちょっとサンプルコード書いて終わりでした。ということで今回が三度目の正直なのですが!!

目から鱗が取れました!

DIコンテナに型の関係を登録するメソッド(RegisterType)を呼び出す際に、LifetimeManagerというオブジェクト渡すようになっています。このLifetimeManagerは抽象クラスなので、インスタンスを生成するにはどうすればよいのか調べてみました。まずは、調べた結果をまとめたクラス図を見てみると

LifetimeManager

拡大版

IBuilderPolicyインターフェイスを継承するILifetimePolicyインターフェイスがあります。そのILifetimePolicyを継承するのがLifetimeManager抽象クラスです。

LifetimeManager抽象クラスからの継承関係を図にしたのが、上記のクラス図になります。

インスタンス化できるLifetimeManagerは下記のとおり

1.ExternallyControlledLifetimeManager
  A that holds a weak reference to it's managed instance.
2.PerThreadLifetimeManager
  A that holds the instances given to it, keeping one instance per thread.
3.TransientLifetimeManager
  An implementation that does nothing, thus ensuring that instances are created new every time.
4.ContainerControlledLifetimeManager
  A that holds onto the instance given to it.When the is disposed, the instance is disposed with it.

※ContainerLifetimeManagerというのもいます。

私のつたない英語力で翻訳してしまうと嘘を書いてしまう恐れがあるのであえて翻訳はしませんが、ものすごく簡単な英語なのでエキサイ・・・いやいや、ふつうに読めますね。DIコンテナが管理するインスタンスのライフタイムを管理するオブジェクトがLifetimeManagerということが大体読み取れます。

これらを平たく言えば

ExternallyControlledLifetimeManager
呼び出した場所のスコープ範囲のライフライムを持つインスタンスを生成する。インスタンスがGCに回収されない限りは、DIコンテナは同一のインスタンスを返す。

PerThreadLifetimeManager
Thread毎のSingleton。

TransientLifetimeManager
単なるFactoryMethod。インスタンスをDIコンテナに要求する度にインスタンスを生成する。new するのと同じ動き。

ContainerControlledLifetimeManager
プロセス単位のSingleton(←間違ってるかもしません)。コンテナ単位で1インスタンス。 IDisposableを実装しているのが特徴。

と、私の狭い知識で知っているDIコンテナのライフタイムと同じような設定が可能なようです。ただし、Unity Application Blockはこれからがすごい!目から鱗です。

ライフタイムを管理するマネージャーがオブジェクト化されている

という点が素晴らしいアイデアだと私は感動しました。DIコンテナの設定情報をどうするか、XAML?属性?なんていう議論が巷ではあるようですが、この設定情報の一部にライフタイムというのが上げられると思います。なんちゃってXMLで記述してみると

<Hoge LifeTime="Singleton">
 <ClassType>...</ClassType>
</Hoge>

みたいな書き方をされると思いますが、LiftTimeは属性値として定義され、要するに静的な単なる値です。これが、Unity Application Blockで提供するライフタイムの管理というのは、明確な境界をもったオブジェクトとして具象化され、アプリケーションの実行中にマネージャーオブジェクトが持つ機能や状態によりDIコンテナの振る舞いや、管理されているインスタンスに影響を与える仕組みが提供できるポテンシャルを持っているということになります。後者は難しいと思いますが(笑)

なのでこのライフタイムのパラダイムの変化は私にとって目からうろこでした。

以上。力説してしまいました。

calendar

S M T W T F S
     12
3456789
10111213141516
17181920212223
24252627282930
31      
<< March 2024 >>

あわせて読みたい

あわせて読みたいブログパーツ

selected entries

categories

archives

recent comment

  • 【WPF】DataGridに編集可能なComboBoxを表示するには?
    art55 (07/16)
  • 【WPF】DataGridに編集可能なComboBoxを表示するには?
    arisa (07/16)
  • 【キーボード】6年前のRealForceを復活させることはできる!?その3
    art55 (05/22)
  • 【キーボード】6年前のRealForceを復活させることはできる!?その3
    分解大好き (05/18)
  • 【.NET Framework 4.5】 IListがIReadOnlyListを継承してない理由。
    art55 (02/04)
  • 【.NET Framework 4.5】 IListがIReadOnlyListを継承してない理由。
    Gen (02/04)
  • 【キーボード】RealForce が壊れて帰ってきた。
    art55 (04/29)
  • 【.NET Framework 4.5】 IListがIReadOnlyListを継承してない理由。
    art55 (02/23)
  • 【.NET Framework 4.5】 IListがIReadOnlyListを継承してない理由。
    かるあ (02/22)
  • 【C#】Dictionaryの実装・データ構造・アルゴリズムを観察する。
    art55 (01/16)

recent trackback

recommend

recommend

recommend

C#プログラマのための.NETアプリケーション最適化技法 (Programmer's SELECTION)
C#プログラマのための.NETアプリケーション最適化技法 (Programmer's SELECTION) (JUGEMレビュー »)
Sasha Goldshtein,Dima Zurbalev,Ido Flatow,サシャ・ゴルドシュタイン,ディマ・ズルバレフ,イド・フラトー

recommend

ろんりと集合
ろんりと集合 (JUGEMレビュー »)
中内 伸光
とてもわかりやすいです。

recommend

recommend

シャノン・ノイマン・ディジタル世界
シャノン・ノイマン・ディジタル世界 (JUGEMレビュー »)
市川 忠男
4章がリレーショナルデータベースな内容になってます。ページ数があまりありませんが、ポイントがものすごく的確にまとまっていて、感動します。

recommend

recommend

東プレ Realforce91UBK-S 静音キーボード 静電容量無接点方式 変荷重 ブラック NG01BS
東プレ Realforce91UBK-S 静音キーボード 静電容量無接点方式 変荷重 ブラック NG01BS (JUGEMレビュー »)

テンキーレス、静音のRealForce91UBK-S。スコスコ感がたまらなく気持ちいいです。家と会社で2台持ってます。

recommend

recommend

プログラミング.NET Framework 第4版 (プログラミングシリーズ)
プログラミング.NET Framework 第4版 (プログラミングシリーズ) (JUGEMレビュー »)
Jeffrey Richter
発売予定美 2013年10月10日。.NET Frameworkとお付き合いする人のバイブルですね。

recommend

recommend

キャット・シッターの君に。
キャット・シッターの君に。 (JUGEMレビュー »)
喜多嶋 隆
私のイラストレータデビュー本です。

recommend

Essential .NET ― 共通言語ランタイムの本質
Essential .NET ― 共通言語ランタイムの本質 (JUGEMレビュー »)
ドン・ボックス,クリス・セルズ,Don Box,Chris Sells,吉松 史彰

links

profile

search this site.

others

mobile

qrcode

powered

無料ブログ作成サービス JUGEM