特集
Enterprise Library 4.0概説

新しいオブジェクト生成機構でEntLibはこう変わる!

アバナード株式会社 市川 龍太(Microsoft MVP 2008 for XML)
2008/06/17
Page1 Page2 Page3

Unity Application Block

 UnityはDIコンテナそのものであり、これ単独で使用することも可能な独立したコンポーネントとなっている(従ってUnityはpatterns & practices - Unity から別途ダウンロードすることができる)。

 DIが何かについては、「.NET開発者のためのDI&AOP入門」を参照するのが一番分かりやすいが、一言で表すなら、DIは依存性を外部から注入するためのパターンであり、DIコンテナとはこのパターンを実現するための仕組みであると認識しておけばよいだろう。

 ちなみに.NETにおける同様のDIコンテナ(および同種の機能を持つフレームワーク)としては、ほかにもS2Containter.NET、Spring.NET、Windsor Containerなどがあるが、UnityはEntLibと同じくpatterns & practicesチームによって開発されたという経緯がある。

 それではここでUnityの使い方について簡単に解説する。まずサンプル用にコンソール・アプリケーションを作成し、以下のコードを追加する。

using Microsoft.Practices.Unity;

namespace EntLib4Study
{
  class Program
  {
    static void Main(string[] args)
    {
      IUnityContainer myContainer = new UnityContainer();
      myContainer.RegisterType<ILog, Log1>();
      ILog logger = myContainer.Resolve<ILog>();
      logger.Output();
    }
  }

  interface ILog
  {
    void Output();
  }

  public class Log1 : ILog
  {
    public void Output()
    {
      Console.WriteLine("サンプル出力1");
    }
  }
}
Resolveメソッドを使用したサンプル・プログラム
以下のアセンブリを参照に追加する
 Microsoft.Practices.Unity.dll

 このサンプル・プログラムを実行すると、コンソール画面に「サンプル出力1」と表示される。これはジェネリック・メソッドであるRegisterTypeメソッドに、インターフェイスと対応するクラスを登録し、ResolveメソッドからILogインターフェイスを実装するLog1クラスのオブジェクトを取得するという基本的なサンプル・プログラムである。

 この程度でUnityがDIコンテナであるというには、いささか無理があるといわれてしまうかもしれないが、さらに次のコードを見てほしい。太字が追記/改変した部分である。

using Microsoft.Practices.Unity;

namespace EntLib4Study
{
  class Program
  {
    static void Main(string[] args)
    {
      IUnityContainer myContainer = new UnityContainer();
      myContainer.RegisterType<ILog, Log1>();
      LogFactory factory =
        myContainer.BuildUp<LogFactory>(new LogFactory());
      ILog logger = factory.GetInstance;
      logger.Output();

      Console.ReadLine();
    }
  }

  interface ILog
  {
    void Output();
  }

  public class Log1 : ILog
  {
    public void Output()
    {
      Console.WriteLine("サンプル出力1");
    }
  }

  public class Log2 : ILog
  {
    public void Output()
    {
      Console.WriteLine("サンプル出力2");
    }
  }

  public class LogFactory
  {
    ILog _logger;

    [Dependency]
    public ILog GetInstance
    {
      get { return _logger; }
      set { _logger = value; }
    }
  }
}
BuildUpメソッドを使用したサンプル・プログラム

 このサンプル・プログラムも「サンプル出力1」と表示する。プログラムでは、LogFactoryクラスの中でLog1またはLog2クラスを生成する処理は一切記述されていないが、GetInstanceプロパティからLog1クラスのオブジェクトが取得できている。

 ちなみに「RegisterType<ILog, Log1>()」を「RegisterType<ILog, Log2>()」に変更すると「サンプル出力2」と表示される。何が起こっているのかというと、BuildUpメソッドがLogFactoryオブジェクトを生成する際に、UnityContainerクラスのRegisterTypeメソッドで事前に登録していたILogインターフェイスに対応するLog2オブジェクトも同時に生成されているのである。

 このカラクリはGetInstanceプロパティに付加されているDependency属性にある。つまりBuildUpメソッドを呼び出した際に、Unityのオブジェクト生成パイプラインの中で各種カスタム属性の有無を確認していき、それら各カスタム属性(この例ではDependency属性)に応じた固有のオブジェクト生成処理が順に実行されていくことで、対象となるオブジェクト(ここではLogFactoryオブジェクト)が生成されていくというメカニズムになっている。

 なお、これらオブジェクト生成のメカニズムには、ObjectBuilder2という別のApplication Blockが使用されており、ObjectBuilderおよびオブジェクト生成パイプラインの詳細については、「Enterprise Library 2.0を特徴づけるDI機能とは」を参考にするとよいだろう(ただし参照先はObjectBuilderの1.0版を対象に記述されているため、2.0版をベースにしているUnityとは多少作りが異なっている点には注意してほしい)。

アプリケーション構成ファイルにDIを設定する

 ここまでの解説から、これら型の対応付けを直接ソース・コードに書くのではなく、外部ファイルに保持できないかと考えた方もいるかもしれないが、もちろんUnityはこれら依存関係をアプリケーション構成ファイル(app.configファイル)に持たせることが可能である。

 以下にアプリケーション構成ファイルの記述例を示す。

<?xml version="1.0" encoding="utf-8"?>
<
configuration>
  <configSections>
    <section name="unity" type=
      "Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,
      Microsoft.Practices.Unity.Configuration, Version=1.1.0.0,
      Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
  </configSections>
  <unity>
    <typeAliases>
      <typeAlias alias="ILog"
        type="EntLib4Study.ILog, EntLib4Study" />
      <typeAlias alias="Log1"
        type="EntLib4Study.Log1, EntLib4Study" />
      <typeAlias alias="Log2"
        type="EntLib4Study.Log2, EntLib4Study" />
    </typeAliases>
    <containers>
      <container name="SampleContainer">
        <types>
          <type type="ILog" mapTo="Log1">
          </type>
        </types>
      </container>
    </containers>
  </unity>
</configuration>
アプリケーション構成ファイルによる依存関係の設定例

 次にこのアプリケーション構成ファイルを使用するサンプル・プログラムの例を以下に示す(Mainメソッド内のソースのみを抜粋)。

static void Main(string[] args)
{
  IUnityContainer myContainer = new UnityContainer();
  UnityConfigurationSection section = (UnityConfigurationSection)
    ConfigurationManager.GetSection("unity");
  section.Containers["SampleContainer"].Configure(myContainer);
  LogFactory factory =
    myContainer.BuildUp<LogFactory>(new LogFactory());
  ILog logger = factory.GetInstance;
  logger.Output();
}
アプリケーション構成ファイルを使用する場合のサンプル・プログラム
・Unityは内部で自動的にアプリケーション構成ファイルを読み込まないので、UnityConfigurationSectionオブジェクトを作成して明示的に読み込む必要がある
・UnityConfigurationSectionオブジェクトのContainersインデクサのキーはアプリケーション構成ファイルのcontainer要素のname属性値(この例では「SampleContainer」)と同じにする

 アプリケーション構成ファイルにUnityの依存設定を記述しておけば、上記サンプル・プログラムにあったRegisterTypeメソッドで型の依存関係を設定しなくても、同様の処理を行うことができる。つまりソース・コードを修正しなくても生成されるオブジェクトを変更可能なのである。

 さらにUnityではコンストラクタやメソッドのパラメータ、プロパティに対しても任意の値を設定できるようになっている。以下にコンストラクタのパラメータに任意の値を設定するアプリケーション構成ファイルの例とサンプル・プログラムを示す。太字の部分が追記/改変した部分である。

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <section name="unity"type=
     "Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,
      Microsoft.Practices.Unity.Configuration, Version=1.1.0.0,
      Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
  </configSections>
  <unity>
    <typeAliases>
      <typeAlias alias="ILog"
        type="EntLib4Study.ILog, EntLib4Study" />
      <typeAlias alias="Log1"
        type="EntLib4Study.Log1, EntLib4Study" />
      <typeAlias alias="Log2"
        type="EntLib4Study.Log2, EntLib4Study" />
    </typeAliases>
    <containers>
      <container name="SampleContainer">
        <types>
          <type type="ILog"mapTo="Log1">
            <typeConfig extensionType=
             "Microsoft.Practices.Unity.Configuration.TypeInjectionElement,
              Microsoft.Practices.Unity.Configuration
">
              <constructor>
                <param name="message"
                  parameterType="System.String">
                  <value value="Hello EntLib 4.0"/>
                </param>
              </constructor>
            </typeConfig>
          </type>
        </types>
      </container>
    </containers>
  </unity>
</configuration>
コンストラクタに値を渡すアプリケーション構成ファイルの設定例

 次にLog1クラスを以下のように変更する。

public class Log1 : ILog
{
  string _message;

  public Log1() { }

  public Log1(string message)
  {
    this._message = message;
  }

  public void Output()
  {
    Console.WriteLine(this._message);
  }
}
引数を受け取るコンストラクタを追加したLog1クラス
引数を受け取るコンストラクタを追加したLog1クラス
 ・Log1クラス以外は変更なし

 このサンプル・プログラムを実行すると、アプリケーション構成ファイルのvalue属性値に設定した“Hello EntLib 4.0”が表示される。

 ここまでの解説でUnityがDIコンテナであることが理解してもらえたと思う。それではこのUnityがEntLibに統合されたことで、既存のEntLibにどのような影響を及ぼしたのだろうか? その答えはオブジェクトの生成アーキテクチャにある。次にそのアーキテクチャについて説明していこう。


 INDEX
  特集:Enterprise Library 4.0概説
  新しいオブジェクト生成機構でEntLibはこう変わる!
    1.EntLib 3.0とEntLib 4.0の相違点
  2.Unity Application Block(1)
    3.Unity Application Block(2)

インデックス・ページヘ  「Enterprise Library 4.0概説」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間