Managed Extensibility Framework(MEF)入門 -AssemblyCatalog-

MEFではコンテナーにある領域のパーツを取り込む為の機構として「カタログ」と言う物が用意されています。今回は「アセンブリ内のパーツを取り込む」為のカタログ『System.ComponentModel.Composition.Hosting.AssemblyCatalog』クラスを取り上げます。AssemblyCatalogクラスはMSDNでこのように説明されています。


AssemblyCatalog は、指定されたアセンブリ内にあるすべてのパーツを解析するために使用されます。ターゲット アセンブリは、オブジェクト参照またはパスによって示すことができます


AssemblyCatalogはコンストラクタで指定されたパス、又はオブジェクト参照を元に指定アセンブリよりパーツを検出します。そのAssemblyCatalogクラスをコンテナーのコンストラクタ引数に設定するとコンテナーはカタログよりパーツを取得しコンテナー内に構成します。

 

assemblycatalog_details

 

簡単なサンプルコードを元に見てみましょう。

C#

using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
 
namespace AssemblyCatalogSample.CSharp
{
   public class Program
   {
      /// <summary>
      /// コンテナ
      /// </summary>
      private CompositionContainer container;
 
      /// <summary>
      /// View群
      /// </summary>
      [ImportMany()]
      public IEnumerable<IView> Views { get; set; }
 
      /// <summary>
      /// コンストラク
      /// </summary>
      private Program()
      {
         //Programクラスが格納されているアセンブリよりパーツを検出する。
         AssemblyCatalog catalog = new AssemblyCatalog(typeof(Program).Assembly);
         //指定カタログを用いてコンテナがパーツを検出する。
         this.container = new CompositionContainer(catalog);
      }
 
      /// <summary>
      /// 処理実行
      /// </summary>
      private void Run()
      {
         //コンテナより直接サービスを取得する。
         IEnumerable<IView> views = this.container.GetExportedValues<IView>();
         foreach (IView view in views)
         {
            Console.WriteLine(string.Format("タイトルは[{0}]です。", view.GetTitle()));
         }
 
         //自分のインスタンスにサービスをインポートして貰う。
         this.container.ComposeParts(this);
         foreach (IView view in this.Views)
         {
            Console.WriteLine(string.Format("タイトルは[{0}]です。", view.GetTitle()));
         }
      }
 
      /// <summary>
      /// エントリポイント
      /// </summary>
      public static void Main()
      {
         Program program = new Program();
         program.Run();
      }
   }
 
   /// <summary>
   /// IViewインターフェース
   /// </summary>
   public interface IView
   {
      /// <summary>
      /// タイトル取得
      /// </summary>
      string GetTitle();
   }
 
   /// <summary>
   /// メインビュー
   /// </summary>
   [Export(typeof(IView))]
   public class MainView : IView
   {
      public string GetTitle()
      {
         return "Main Window";
      }
   }
 
   /// <summary>
   /// メッセージダイアログ
   /// </summary>
   [Export(typeof(IView))]
   public class MessageDialog : IView
   {
      public string GetTitle()
      {
         return "Message Dialog";
      }
   }
}
VB

Imports System
Imports System.ComponentModel.Composition
Imports System.ComponentModel.Composition.Hosting
 
Public Class Program
   ''' <summary>
   ''' コンテナー
   ''' </summary>
   Private container As CompositionContainer
 
   ''' <summary>
   ''' View群
   ''' </summary>
   <ImportMany()> _
   Public Property Views() As IEnumerable(Of IView)
 
   ''' <summary>
   ''' エントリポイント
   ''' </summary>
   Public Shared Sub Main()
      Dim program As New Program()
      program.Run()
   End Sub
 
   ''' <summary>
   ''' コンストラク
   ''' </summary>
   Private Sub New()
      'Programクラスが格納されているアセンブリよりパーツを検出する。
      Dim catalog As New AssemblyCatalog(GetType(Program).Assembly)
      '指定カタログを用いてコンテナがパーツを検出する。
      Me.container = New CompositionContainer(catalog)
   End Sub
 
   ''' <summary>
   ''' 処理実行
   ''' </summary>
   ''' <remarks></remarks>
   Private Sub Run()
      'コンテナより直接サービスを取得する。
      Dim views As IEnumerable(Of IView) = Me.container.GetExportedValues(Of IView)()
      For Each view As IView In views
         Console.WriteLine(String.Format("タイトルは[{0}]です。", view.GetTitle()))
      Next
 
      '自分のインスタンスにサービスをインポートして貰う。
      Me.container.ComposeParts(Me)
      For Each view As IView In Me.Views
         Console.WriteLine(String.Format("タイトルは[{0}]です。", view.GetTitle()))
      Next
   End Sub
End Class
 
''' <summary>
''' IViewインターフェース
''' </summary>
Public Interface IView
   ''' <summary>
   ''' タイトル取得
   ''' </summary>
   Function GetTitle() As String
End Interface
 
''' <summary>
''' メインビュー
''' </summary>
<Export(GetType(IView))> _
Public Class MainView
   Implements IView
   ''' <summary>
   ''' タイトル取得
   ''' </summary>
   Public Function GetTitle() As String Implements IView.GetTitle
      Return "Main View"
   End Function
End Class
 
''' <summary>
''' メッセージダイアログ
''' </summary>
<Export(GetType(IView))> _
Public Class MessageDialog
   Implements IView
   ''' <summary>
   ''' タイトル取得
   ''' </summary>
   Public Function GetTitle() As String Implements IView.GetTitle
      Return "Message Dialog"
   End Function
End Class

サンプルコードを実行すると以下のように出力されます。

 

assemblycatalog_details_output

 

サンプルコードでは、Programクラスが格納されているアセンブリ内のパーツ群をAssemblyCatalogクラスを使用してコンテナーに取り込んでいます。今まで取り上げてきた記事では『System.ComponentModel.Composition.Hosting.CompositionBatch』クラスを使用してパーツをCompositionBatchクラスに追加していました。それではアプリケーションで使用するパーツ群を構成する為には一つずつ追加しなけらばならず、効果的とは言えません。カタログを使用する事によってある領域のパーツを取り込む事が出来ます。サンプルコードから分かるようにAssemblyCatalogクラスを使用すると、指定したアセンブリからパーツ群を検出して取り込む事が出来ます。MEFでは一般的にカタログを使用してパーツをコンテナーに取り込みます。「System.ComponentModel.Composition.Hosting名前空間には様々なカタログが用意されていますので、用途に合わせてカタログを使い柔軟なパーツ構成を行えます。是非試してみては如何でしょうか。

 

Source code