ページ

2010年8月24日火曜日

[Silverlight] MEF を使って XAP を動的に読み込む その1

前の記事で Silverlight 4 では MEF (Managed Extensibility Framework) を使って XAP を動的に読み込めばいいんじゃないかと書きましたが、せっかくなのでサンプルを紹介しときます。
内容自体は VSUG Day 2010 Summer 大阪 のセッションで紹介したのとだいたい同じだったりしますが。

まず、MEF っていうのはプラグインといった仕組みを実現するフレームワークだと思ってもらえばいいんじゃないかと思います。結構いろいろな機能があるため全体としてはそれなりにややこしくなっていますが、基本的な部分を使うだけならば、いくつかの約束ごとを覚えればなんとかなる感じです。
私も全体をきちんと理解出来ているわけではありませんが、単純な例と XAP を動的に読み込む例の 2回に分けて MEF の概要を紹介してみます。
なお、MSDN ライブラリの解説は 「Managed Extensibility Framework の概要」 などにありますから詳しい話はそちらを参照してみてください。(残念ながら MSDN ライブラリの解説もすべてを網羅してるわけではありませんが)
ちなみに、MEF は 「メフ」 と発音されることが多いみたいです。(Microsoft の人のチュートリアル動画を見てたらそう発音してた)

では、まず手始めに単純な例。
(C# で書いてますがもちろん VB でも同じです)

なお、Visual Studio 2010 のソリューションを http://cid-ca42d76a68f54d16.office.live.com/self.aspx/Public/Sample/MefSample.zip に置いておきます。
ZIP ファイル内の MefSample01 フォルダが以下の例です。

Visual Studio 2010 で Silverlight 4 プロジェクトを作ります。
MEF の多くは System.ComponentModel.Composition.dll に含まれてますので、まずはこれを参照設定します。
続いて以下のような内容の Person.cs を追加。

using System.ComponentModel.Composition;

namespace MefSample01
{
    public class Person
    {
        public Person()
        {
            this.Name = "MEFサンプル";
        }

        [Export("PersonName")]
        public string Name { get; set; }
    }
}

次に Silverlight のメインページのクラス (MainPage.xaml.cs) を以下のように書き換えます。

using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;

namespace MefSample01
{
    public partial class MainPage : UserControl
    {
        [Import("PersonName")]
        public string personName;

        public MainPage()
        {
            InitializeComponent();

            var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
            var container = new CompositionContainer(catalog);
            container.ComposeParts(this);

            MessageBox.Show(this.personName);
        }
    }
}

これで実行してみると実行直後に “MEFサンプル” という内容のメッセージボックスが表示されるはずです。
このメッセージボックスは personName フィールドの内容を表示してるんですが、一見すると personName フィールドに内容をセットするコードが見当たりません。もちろん、本当にセットされていなかったら null 参照で例外が出ちゃうので、どこかではセットされてるんですが。
それをやってるのが

var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
var container = new CompositionContainer(catalog);
container.ComposeParts(this);

この 3行です。そして、この 3行が MEF のキモと言っていい部分です。

まず 1行目。ここでカタログを作っています。カタログというのは 「エクスポートしたいもの」 の所在を指定するもの、と思ってもらえばいいんじゃないかと思います。上のサンプルでは AssemblyCatalog を使って今現在実行中のアセンブリをカタログとして指定しています。(要するに自分自身をカタログに指定しているってことです) カタログには、AssemblyCatalog の他にも、ディレクトリの中のアセンブリを探してくれる DirectoryCatalog、XAP をダウンロードしてくれる DeploymentCatalog などがいくつかの種類があります。
次の 2行目でコンテナを作っています。
そして、3行目でこのコンテナに対して自分 (this) のインポートを解決するように依頼しています。
この 2行目と 3行目はお約束と思ってもらっていいんじゃないかと思います。

この 「カタログ作って、コンテナにカタログ入れて、そのコンテナにインポートの解決を依頼」 というのが MEF のキモなわけですが、もうひとつのキモがインポートとエクスポートの指定です。

MainPage クラスの personName フィールドには Import 属性が、そして、Person クラスの Name プロパティには Export 属性が付いています。
MEF のインポートの解決とは、

  • container.ComposeParts() メソッドに渡された引数のオブジェクト (今回の場合は this) の中の Import 属性が付いているものを探す。
  • コンテナの中の Export 属性が付いているものを探す。
  • 名前と型が一致するものがあったらエクスポート側から値を取り出し、インポート側にセットする。

ということです。
ちなみに、エクスポート側から値を取り出し、インポート側にセットするというのは、具体的に書くと

var person = new Person();
this.personName = person.Name;

というのと同じことが実行されています。

あと、MEF のサンプルを見ていると、上記の 3行が

var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
CompositionHost.Initialize(catalog);
CompositionInitializer.SatisfyImports(this);

こんな感じに書かれているものがあります。
これは自分で明示的にコンテナを作らずに、MEF に内蔵されているデフォルトのコンテナを使っているということみたいです。ただ、デフォルトのコンテナを使うことにどんな利点があるのかは私は良くわかってません。
ちなみに CompositionHost などを使う場合は System.ComponentModel.Composition.Initialization を参照設定する必要があります。

この例ではプロパティを Export しましたが、クラスを Export することもできます。
次の例ではクラスを Export し、XAP をコンテナとして指定して動的に XAP を読み込むようにしてみます。

[Silverlight] MEF を使って XAP を動的に読み込む その2」 に続きます。

0 件のコメント:

コメントを投稿