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を実現できる実装をする予定。