IronPythonでAOPモドキ

http://d.hatena.ne.jp/akiramei/20060723/p3の続き。

最初に知っておかないといけないことは、IronPythonのクラス(型)が.NET上では型として認識されないということです。IronPythonのクラスはメンバーとしてDictionaryを持っていて、その中にプロパティやらメソッドやらを放り込んでいたりします。このように、IronPythonのクラスはCLR世界とは大きく異なっているため、クラスでのやり取りは厳しそうです。一方、関数については比較的シームレスにやり取りできるので、delegateやeventを使うのが楽そうです。
AOPの為には、メソッド呼び出しに割り込む必要があります。常套手段としてProxyオブジェクトを用意して割り込むのですが、先に述べたようにIronPythonでクラスを作成してもCLRには渡せません(渡しても使いにくい)。しかし、柔軟性を確保するにはIronPython側でProxyオブジェクトを作成したいです。

castle projectにDynamicProxyというProxy生成用のライブラリがありますので、今回はそれを使うことにします。DynamicProxyを使って割り込むにはIInterceptorインタフェースを実装するクラスを用意すれば良いのですが、IronPythonで割り込みコードを書けるようにちょっと細工します。

using Castle.DynamicProxy;

public class MyProxyInterceptor : IInterceptor
{
    public delegate object ProceedFunction(IInvocation invocation, object args);

    // IronPythonから呼び出せるようにイベントを公開する
    public event ProceedFunction Proceed;

    public object Intercept(IInvocation invocation,
      params object args)
    {
        object returnVal = null;

        if (Proceed == null)
            returnVal = invocation.Proceed(args);
        else
            returnVal = Proceed(invocation, args);

        return returnVal;
    }
}

Proceedイベントがミソで、これを経由してIronPythonとやりとりさせます。では、先日のIGreetingViewの生成コードを修正して、メソッドの呼び出しに割り込んでみましょう。

from clr import *
from ConsoleApplication3 import *
from Castle.DynamicProxy import *

# メソッド呼び出しに割り込んで実行する関数
def proceed (invocation, args):
  print 'Before'
  args[0].Message += ' in Python'
  obj = invocation.Proceed(args)
  print 'After'
  return obj

# Pythonの関数をイベントに登録
interceptor = MyProxyInterceptor()
interceptor.Proceed += proceed

# Proxyオブジェクトの生成
implement = GreetingConsoleView()
py_IGreetingView = ProxyGenerator().CreateProxy(
  GetClrType(IGreetingView), interceptor, implement)

IronPythonで書いた関数をProceedイベントに登録しています。これによって、IGreetingView.Showの呼び出しに割り込んで、Beforeを出力し、引数のメッセージを書き換えて、本来のメソッドをコール。最後にAfterを出力しています。

IronPythonではオブジェクトの生成しか行わないという整理にするのなら、このようなイベントを用意する必要はありません。

IronPythonは.NET上の開発言語として考えた場合、Pythonコードの資産がなければ、それほど魅力的な選択肢ではないかもしれません。Python風の文法が好みなら、Booを使い事も出来、こっちの方は他の言語から呼び出せるアセンブリを生成できます。しかし、視点を変えてXMLのようなデータ変換目的と考えた場合、こと、オブジェクトを扱うことに関しては、強力なツールとなりうると思います。

プロジェクトの縁の下の力持ちとして使ってみては如何でしょうか?(^^;