プロクシを使ってAOP

どっかの記事で「C#はAttribute(属性)を使えばAOPを利用できる。」てのをみたんだけれども。
どう実現するのか見当もつかなかったわけで。
Javaと同じでプロクシ使うんじゃないかなぁとだけ漠然と。
で、S2AOP.NET概要をみてたら答えがw
System.Runtime.Remoting.Proxies.RealProxy を使うらしい。
さらにGoogle先生に聞いてみたら、ビンゴなサイトを発見。
Device Hook(リンクしていいのかわからないので問題があれば削除しま)
上記サイトを参考にサンプル実装してみた。


1.AOPを実行するファクトリクラス
RealProxyのサブクラスとして作成。
覚え立てのジェネリクスも使ってみたw

using System;
using System.Runtime.Remoting.Proxies;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting;
using System.Reflection;
using System.Diagnostics;

namespace ProxySample
{
    public class SampleProxy : RealProxy
    {
        public static T CreateTarget<T>() where T : MarshalByRefObject
        {
            T target = Activator.CreateInstance<T>();
            SampleProxy proxy = new SampleProxy(typeof(T));
            return (T)proxy.GetTransparentProxy();
        }

        /// 処理対象クラスはMarshalByRefObjectのサブクラス
        private readonly MarshalByRefObject target;

        private SampleProxy(Type type) : base(type)
        {
            target = (MarshalByRefObject)Activator.CreateInstance(type);
        }

        public override IMessage Invoke(IMessage msg)
        {
            Debug.WriteLine("intercept start");
            // リモートサービスによる実行
            IMethodReturnMessage rtnMessage =
                RemotingServices.ExecuteMessage(target, call);
            Debug.WriteLine("intercept end");
            return rtnMessage;
        }
    }
}

利用する側は SampleProxy.CreateTarget(); を呼び出してインスタンスを取得。
はRealProxyの制限で MarshalByRefObject のサブクラスでなければダメ。
Invokeメソッドは、インスタンスのメソッドを呼び出したときに実行される。
つまり、ここが割り込み可能ポイント。


2.サンプル実行クラスと単純なオブジェクト

private void button1_Click(object sender, EventArgs e)
{
    TargetBean t = SampleProxy.CreateTarget<TargetBean>();
    t.PrintMessage("Hello AOP");
}
public class TargetBean : MarshalByRefObject
{
    public TargetBean() { }
    public void PrintMessage(string text)
    {
        Debug.WriteLine("PrintMessage=" + text);
    }
}

これを実行すると

intercept start
PrintMessage=Hello AOP
intercept end

こんな感じでちゃんと割り込みされる。


3.Adviceぽいものをためしてみる。
といってもカスタム属性を作って呼び出しメソッドに付けるだけ。
Invokeメソッドの中でリフレクション使って対象メソッドから属性を取得してごにょごにょする。


こんな感じでひとまずそれっぽいファクトリクラスができたので、あとはいろいろ手を加えてみたいところ。
TODOとしては以下の感じ?

  • ステートレスなインスタンス管理
  • Adviceの種類を分ける(Before, After, Around, Traceとか?)
  • IMessageで受け取った実メソッドの引数に対する操作

毎度のことながらこれを生かす場所がない、完全な自己満足ってのがなんだかなぁ(´・ω・`)