DynamicProxy - C#でmixin
この日記で何度か紹介しているCastle.DynamicProxyだが、今まではAOPにおけるメッセージのインターセプターの手段として採り上げてきた。しかし、本来DynamicProxyはAOPにだけ使うものではなく、動的に型を拡張する、型に対するメッセージ(メソッド)をインターセプトする、という特徴から他にもいろいろなことに応用できる。その一つがmixinの実現だ。
mixin(ミックス・イン Mix-In )はRuby等で採用されているコードの再利用の仕組みであり、他の実装を型に混ぜ込む(mixed-in)ことにより、複数の(多重の)実装を継承できる機能だが、DynamicProxyが提供するProxyGeneratorクラスとGeneratorContextクラスによって簡単に実現できる。
mixinの対象はオブジェクトだが、なんらかのインタフェースを実装している必要がある。ここではmixinの対象として、二つのインタフェースとその実装クラスを用意した。
public interface IMooable { void Moo(string moo); } public interface IBowable { void Bow(string bow); } public class MooImpl : IMooable { public void Moo(string moo) { Console.Out.WriteLine(moo); } } public class BowImpl : IBowable { public void Bow(string bow) { Console.Out.WriteLine(bow); } }
これらのmixin対象(MooableとBowable)を使って、MooBowキメラをDynamicProxyで作るコードは以下のようになる。生成した型のチェックが解り易いように、nUnitのAssertクラスのメソッドを使用してみた。
namespace Mixin { [TestFixture] public class MixinTest : IInterceptor { [Test] public void CreateMixin() { ProxyGenerator generator = new ProxyGenerator(); GeneratorContext context = new GeneratorContext(); //mixinの対象を生成 MooImpl moo = new MooImpl(); BowImpl bow = new BowImpl(); //generatorContextにmixin対象を追加 context.AddMixinInstance(moo); context.AddMixinInstance(bow); //mixinを使って、MixinTestクラスを拡張したオブジェクトを生成 object mooBow = generator.CreateCustomClassProxy( typeof(MixinTest), this, context, false); //以下のテストは全て成功する Assert.IsNotNull(mooBow); Assert.IsTrue(typeof(MixinTest).IsAssignableFrom(mooBow.GetType())); IMooable mooable = mooBow as IMooable; Assert.IsNotNull(mooable); mooable.Moo("moo"); IBowable bowable = mooBow as IBowable; Assert.IsNotNull(bowable); bowable.Bow("bow"); } #region IInterceptor member public object Intercept(IInvocation invocation, params object[] args) { return invocation.Proceed(args); } #endregion } }
DynamicProxyが動的に生成したmixinされた型の名前は
CProxyTypeMixinMixintestMixin_MixinIMooable_MixinIBowable2
という独特な名前になる。この名前から想像できるが、生成された型の内部にIMooableとIBowableを取り込んでいることが解るだろう。(mixinは実際に混ぜ込んだオブジェクトを内部にリストで持ち、インターセプトされたメッセージによってmixinされたオブジェクトに処理を委譲することになる)
mixinを作るだけなのに、AOPの時と同様にIInterceptorの実装を強制されるのはどうかと思う。インターセプタのヌルパターン実装があれば良いと思うのだが、これは開発元でも既に懸案になっているようだDynamicProxy 2 Brainstorm
- Null interceptor (i.e. no interception, only mixins)
今回のようにコードでmixinを作るのも良いが、やはり、やりたいのはDIコンテナを使ったmixinの宣言的な構成だ。
こんな感じで、簡単にmixinを実現できる実装をする予定。