MixIn Interfaces Proxy(その3)

コメント欄だと書きにくいので、エントリとして書くことに。

# sugimotokazuya 『1つ問題がありました。Mixinによるプロキシだと実装クラスの型で受け取れない(キャストできない)という制限がありました。』

とのことだが、実装クラスの型でも受け取りたいのであれば、一番ベーシックな、第一パラメタがTypeであるDynamicAopProxyのコンストラクタを使えば良いと思うのだが、いかがだろう。

昨日、デモとして書いたプログラムのMainメソッドを、今日の話向けに書き直してみた。

static void Main(string args)
{
    IMethodInterceptor interceptor = new TraceInterceptor();
    IPointcut pointcut = new PointcutImpl(new string { ".*" });
    IAspect aspects = new AspectImpl(interceptor, pointcut);
    DynamicAopProxy proxy1 =
          new DynamicAopProxy(typeof(TestImpl), new IAspect[] { aspects });
    ITest1 itest1 = proxy1.Create() as ITest1;
    itest1.TestFunctionOne("Hello ITest1");

    ITest2 itest2 = itest1 as ITest2;
    itest2.TestFunctionTwo("Hello", "ITest2");

    ITest3 itest3 = itest1 as ITest3;
    itest3.TestFunctionThree("Hello", "ITest3", "yah!");

    TestImpl itestimpl = itest1 as TestImpl;
    itestimpl.TestFunctionOne("Hello TestImpl");
    itestimpl.TestFunctionTwo("Hello ", "TestImpl");
    itestimpl.TestFunctionThree("Hello", "TestImpl", "yah!");
    System.Environment.Exit(0);
}

この例であれば、DynamicProxyで拡張した型から生成したオブジェクトはITest1、ITest2、ITest3、そしてTestImplと、全ての型にキャストが可能だ。
と、ここまで書いていて思い出したが、そもそもインタフェースのプロキシが欲しかったのは、DynamicProxyを利用したDynamicAopProxyクラスのプロキシの場合、AOPの対象メソッドはvirtual修飾されたものだけに制限される、というのを回避することだった。

であれば、この方法は使えない。試しに、TestImplクラスのメソッドからvirtual修飾を外すと上のサンプルの実行結果は

TestFunctionOne(Hello ITest1)
TestFunctionTwo(Hello, ITest2)
TestFunctionThree(Hello, ITest3, yah!)
TestFunctionOne(Hello TestImpl)
TestFunctionTwo(Hello , TestImpl)
TestFunctionThree(Hello, TestImpl, yah!)

このようにインターセプタが織り込まれない。当たり前だが駄目だ。
ちなみに、DynamicProxyは、複数のインタフェースをパラメタにとるクラスプロキシを作ることもできるのだが

//インタフェースの配列を第一パラメタにとる、コンストラクタ内部

this.enhancedType = this.generator.ProxyBuilder.CreateClassProxy(this.target.GetType(), interfaces);

このように生成したプロキシはインタフェースのメソッドと、クラスのvirtual修飾されたメソッドがプロキシ上で重複してしまうのか、メソッドの実行で永久ループを起こし、StackOverflow例外を投げるので、使いものにならなかった。

そうなると、今のところ思いつくのはクラス用のプロキシと、インタフェースのプロキシを別々に作ること位だろうか。

static void Main(string args)
{
    IMethodInterceptor interceptor = new TraceInterceptor();
    IPointcut pointcut = new PointcutImpl(new string { ".*" });
    IAspect aspects = new AspectImpl(interceptor, pointcut);
    DynamicAopProxy proxy_forInterfaces =
        new DynamicAopProxy(
            new Type { typeof(ITest1), typeof(ITest2), typeof(ITest3) },
            new IAspect { aspects }, null, new TestImpl());
    ITest1 itest1 = proxy_forInterfaces.Create() as ITest1;
    itest1.TestFunctionOne("Hello ITest1");
    ITest2 itest2 = itest1 as ITest2;
    itest2.TestFunctionTwo("Hello", "ITest2");
    ITest3 itest3 = itest1 as ITest3;
    itest3.TestFunctionThree("Hello", "ITest3", "yah!");
    
    DynamicAopProxy proxy_forClass = new DynamicAopProxy(typeof(TestImpl), new IAspect[] { aspects });
    TestImpl itestimpl = proxy_forClass.Create() as TestImpl;
    itestimpl.TestFunctionOne("Hello TestImpl");
    itestimpl.TestFunctionTwo("Hello ", "TestImpl");
    itestimpl.TestFunctionThree("Hello", "TestImpl", "yah!");
    System.Environment.Exit(0);
}

これならば要件は満たせるが、少々無駄だ。Mixinなプロキシを作ること自体はできるはずなので、時間を見てもう少し調べてみよう。