DI コンテナ Unity の利用(その1)

いまさらですが、DI コンテナの Unity について、使い方などをちょっと調べてみたので、備忘録を兼ねて書いてみます(統合型のゲーム開発環境の Unity の話ではありません。念のため 😉 )。

言うまでもなく DI コンテナのほうの Unity は、Dependency Injection(依存性の注入)の機能を提供するフレームワークで、コンストラクター注入、プロパティ注入、メソッド呼び出し注入の機能がありますが、ここでの話はコンストラクター注入に絞り込みます。

まずは、基本的な構成で、Sample クラスに InjectedClass を注入する場合の例です。後始末が必要になるときのために、IDisposable インターフェイスも実装させ、Dispose のさせかたも例示します。

コンソール アプリケーションのプロジェクトを作成し、NuGet から Unity を導入します。

最初に IInjectedClass インターフェイスから。

namespace SimpleExampleUnityDi
{
    public interface IInjectedClass
    {
        int Number { get; set; }
    }
}

次に、InjectedClass です。

using System;

namespace SimpleExampleUnityDi
{
    public class InjectedClass : IInjectedClass, IDisposable
    {
        private int _number;
        public int Number
        {
            get { return _number; }
            set { _number = value; }
        }

        public void Dispose()
        {
            Console.WriteLine("Disposed InjectedClass instance!");
        }
    }
}

IDisposable インターフェイスを実装し、Dispose() メソッド中で dispose が行われたメッセージを出すようにしています。

次に、Sample クラスです。

using System;

namespace SimpleExampleUnityDi
{
    public class Sample : IDisposable
    {
        public Sample(IInjectedClass injected)
        {
            _injected = injected;
        }

        private readonly IInjectedClass _injected;

        public void SetValue(int number) => _injected.Number = number;

        public int GetValue() => _injected.Number;

        public void Dispose()
        {
            (_injected as IDisposable)?.Dispose();
        }
    }
}

このクラスがコンストラクタで IInjectedClass インターフェイスの実装クラスの注入を受けるクラスになります。

このクラスも IDisposable インターフェイスを実装し、 Dispose() メソッド中で、注入された IInjectedClass インターフェイスの実装クラスが IDisposable インターフェイスを実装していれば、Dispose() メソッドを呼び出すようにしています。この部分の実装の意味するところは、後ほど説明します。

最後に、Program クラスです。

// 拡張メソッドとして実装されているものがあるため、using 設定が必要
using Microsoft.Practices.Unity;

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SimpleExampleUnityDi
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var container = new UnityContainer())
            {
                // RegisterType メソッドでライフタイムを指定しない場合、PerResolve(呼び出しの都度インスタンスを生成)になる。
                container.RegisterType<IInjectedClass, InjectedClass>();

                // DI コンテナが生成するインスタンスが dispose を必要とする場合には、明示的に dispose を行う必要がある。
                using (var sample = container.Resolve<Sample>())
                {
                    sample.SetValue(10);

                    Console.WriteLine($"Number: {sample.GetValue()}");
                }
            }

            Console.WriteLine("Press any key...");
            Console.ReadKey();
        }
    }
}

container.RegisterType<IInjectedClass, InjectedClass>() メソッドで、コンストラクタ呼び出しに必要となる IInjectedClass インターフェイスに対して、InjectedClass クラスのインスタンスを注入するように設定しています。
そして、var sample = container.Resolve<Sample>(); にて、container.Resolve<Sample>() メソッドを呼び出して、Sample クラスのインスタンスに InjectedClass クラスのインスタンスを注入(Sample クラスのコンストラクタ Sample(IInjectedClass injected) の引数に InjectedClass クラスのインスタンスをセットしてインスタンスを生成)して、sample 変数へ格納しています。

コメントに書いているように、UnityContainer クラスの RegisterType メソッドでライフタイムを指定しない場合、PerResolve(呼び出しの都度、注入されるクラスのインスタンスを新たに生成)になります。PerResolve の場合、注入されるインスタンスが dispose を必要とする場合には、明示的に dispose を行う必要があります。コンストラクタ注入では、呼び出しの都度、コンストラクタ注入を行ったインスタンスを取得することになるので、当該インスタンスの Dispose() メソッドの中で dispose されるようにすればよいでしょう(Sample クラスで説明した、「IDisposable インターフェイスを実装していれば、Dispose() メソッドを呼び出すようにしています。」の部分です)。

また、Unity クラスの拡張メソッドとして実装されているものがあるため、Microsoft.Practices.Unity 名前空間への using 設定が必要になります。

このプログラムを実行したときのコンソール出力のスクリーンショットを示します。

SimpleExampleUnityDi 実行時のスクリーンショット

次回は、ライフタイムに ContainerControlledLifetimeManager(コンテナの生存期間と同一)を設定した場合について説明します。


1 thought on “DI コンテナ Unity の利用(その1)

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です