Managed Extensibility Framework入門もついに10回になりました。結構長いものです。今回は、MEFのコンテナ内で作成されたインスタンスの初期化処理について書きます。
通常のインスタンスの初期化
オブジェクト指向言語とうたってる言語では、全ての言語が兼ね備えてる初期化処理があります。俗にいうコンストラクタですね。MEFで管理されるインスタンスも、インスタンスが作成されるタイミングでコンストラクタが実行されるので、初期化処理を書くことが出来ます。しかし、この状態だと不都合なケースがあります。以下に例を示します。
namespace MEFInitSample { using System; using System.ComponentModel.Composition; using System.ComponentModel.Composition.Hosting; using System.Reflection; class Program { static void Main(string[] args) { // コンテナ初期化 var c = new CompositionContainer( new AssemblyCatalog(Assembly.GetExecutingAssembly())); // PersonClientを取得する Console.WriteLine("PersonClient取得前"); var client = c.GetExportedValue<PersonClient>(); Console.WriteLine("PersonClient取得後"); } } [Export] public class PersonClient { [Import] public Person Person { get; set; } public PersonClient() { // ここでPersonを使って処理をしたい! Console.WriteLine("PersonClient.ctor() called"); Console.WriteLine(" this.Person = " + this.Person); // この時点ではthis.Personはnull } } [Export] public class Person { } }
特に何か処理をするプログラムではありませんが、PersonClientクラスがPersonクラスをMEF経由で設定されるように構成しています。この状態でコンストラクタで初期化処理をしようとすると、その時点では、まだPersonClientクラスのPersonプロパティには値が設定されていません。
MEFから依存性の注入をしてもらったタイミングで処理をやる方法がないと、ちょっと困りそうです。役者が揃わないと仕事が出来ませんからね。
依存性注入直後に処理をする方法
そのための仕掛けとしてMEFではSystem.ComponentModel.Composition.IPartImportsSatisfiedNotificationインターフェースを提供しています。MSDNでは以下のように説明されています。
パーツのインポートが満たされたときに、そのパーツに通知します。
このインターフェースはOnImportsSatisfiedという引数も戻り値も無いメソッドが定義されています。このメソッドが、MEFのコンテナ内で初期化が終わったタイミングで呼び出されます。
実装してみよう
ということで、先ほどのPersonClientにこのインターフェースを実装して書き換えてみます。
namespace MEFInitSample { using System; using System.ComponentModel.Composition; using System.ComponentModel.Composition.Hosting; using System.Reflection; class Program { static void Main(string[] args) { // コンテナ初期化 var c = new CompositionContainer( new AssemblyCatalog(Assembly.GetExecutingAssembly())); // IPartImportsSatisfiedNotification.OnImportsSatisfiedの動作確認 Console.WriteLine("PersonClient取得前"); var client = c.GetExportedValue<PersonClient>(); Console.WriteLine("PersonClient取得後"); } } [Export] public class PersonClient : IPartImportsSatisfiedNotification { [Import] public Person Person { get; set; } public PersonClient() { Console.WriteLine("PersonClient.ctor() called"); Console.WriteLine(" this.Person = " + this.Person); } public void OnImportsSatisfied() { // ここでは、Personクラスの設定が完了しているので好きなように使える Console.WriteLine("PersonClient.OnImportsSatisfied() called"); Console.WriteLine(" this.Person = " + this.Person); } } [Export] public class Person { } }
先ほど説明したIPartImportsSatisfiedNotificationをPersonClientに実装しています。OnImportsSatistfiedでPersonプロパティの値を表示しています。これを実行すると、以下の結果が表示されます。
PersonClient取得前 PersonClient.ctor() called this.Person = PersonClient.OnImportsSatisfied() called this.Person = MEFInitSample.Person PersonClient取得後
最初のthis.Personの出力は、まだ値が設定されていないので何も表示されていません。そして、OnImportsSatisfiedではインスタンスが設定されているので、きちんと表示されています。
ということで、MEFから設定されるインスタンスが設定されたあとに初期か処理を行いたいときはIPartImportsSatisfiedNotificationを実装するということを今回は習得しました。
サンプルプログラム
サンプルプログラムは以下のページからダウンロードできます。
http://code.msdn.microsoft.com/Managed-Extensibility-3332350b
過去の記事
- Managed Extensibility Framework入門 その1「はじめに」
- Managed Extensibility Framework入門 その2「使うに当たって覚えておきたいこと」
- Managed Extensibility Framework入門 その3「Export」
- Managed Extensibility Framework入門 その4「もっとExport」
- Managed Extensibility Framework入門 その5「Import」
- Managed Extensibility Framework入門 その6「拡張可能なアプリケーション作成」
- Managed Extensibility Framework入門 その7「クラス以外のExportとImport」
- Managed Extensibility Framework入門 その7.5「遅延初期化のLazy」
- Managed Extensibility Framework入門 その8「遅延初期化」
- Managed Extensibility Framework入門 その9「メタデータ」