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だと動かないので試行錯誤になりましたが、多分、こんな感じ。(^^;