MEFでオープンジェネリック型をエクスポート

MefContribにはジェネリック用のGenericCatalogクラスがあります。これを使うとオープンジェネリック型をエクスポートすることができます。ただ、ドキュメント不足な上に、ちょっと癖がありそうです。

公開するインタフェースは、こんな感じです。

    // このインタフェースから継承したクラスはすべてエクスポートされる
    [InheritedExport]
    public interface ICalc<T>
    {
        T Add(T x, T y);
        T Div(T x, T y);
    }

色々試した限りでは、実装クラスにExport属性を付けてもうまくいかず、インタフェースにInheritedExport属性を付ける必要がありました。とりあえず、オープンジェネリック型をエクスポートする場合は、インタフェースにInheritedExport属性を付けましょう。

実装は適当に。

    // このエクスポートは発見されない。
    // ネット上のサンプルを見る限り、できそうなんだけど・・・
    // [Export(typeof(ICalc<>))]
    public class CalcImpl<T> : ICalc<T>
    {
        public T Add(T x, T y)
        {
            return (dynamic)x + y;
        }

        public T Div(T x, T y)
        {
            return (dynamic)x / y;
        }
    }

テストなのでdynamicを使ってごまかしておきます。

次にジェネリックインタフェースと実装クラスのマッピングを定義するクラスを用意します。GenericCatalogにこのクラスを渡すことで型を解決できるようになるようです。

    /// <summary>
    /// open genericの場合、実装クラスとのマッピングを指定する必要がある。
    /// </summary>
    [Export(typeof(IGenericContractRegistry))]
    public class MyGenericContractRegistry : GenericContractRegistryBase
    {
        protected override void Initialize()
        {
            Register(typeof(ICalc<>), typeof(CalcImpl<>));
        }
    }

準備ができたので、さっそく使ってみましょう。

    [Export]
    class Program
    {
        [Import]
        public ICalc<int> CalcInt32 { get; set; }
        
        [Import]
        public ICalc<double> CalcDouble { get; set; }

        public void Run()
        {
            int x = 1, y = 2;
            Console.WriteLine("{0} / {1} = {2}", x, y, CalcInt32.Div(x, y));
            Console.WriteLine("{0} / {1} = {2}", x, y, CalcDouble.Div(x, y));
        }

        static void Main(string[] args)
        {
            var catalog = new TypeCatalog(typeof (Program));
            var generic = new GenericCatalog(catalog, new MyGenericContractRegistry());
            var container = new CompositionContainer(generic);
            var program = container.GetExportedValue<Program>();

            program.Run();
        }
    }
/* 結果
1 / 2 = 0
1 / 2 = 0.5
 */

CodePlexのフォーラムやブログにあったサンプルが今のMefContribだと動かないので試行錯誤になりましたが、多分、こんな感じ。(^^;