技術をかじる猫

適当に気になった技術や言語、思ったこと考えた事など。

素の .NET で Aspect 指向したくてがんばってみた。

C# .NETアスペクト指向がしたくて根性出してみた。
あぁ、Python ならデコレータで一発なのに、、、、とかやってて思った。*1

実装方法的には、RealProxy 噛ませば、メソッドコールを基本すべて横取りできる。
で、RealProxy ラップすると、GetTransparentProxy()拾ってこないと処理できない。

SampleTarget target = new SampleTarget();
RealProxy sample = new SampleProxy(target, target.GetType());
target = (SampleTarget)sample.GetTransparentProxy();

美しくはないので、Attribute かけといて、インスタンス化したときに自動適用する。
まずは、呼び出し元。

class Program
{
    static void Main(string[] args)
    {
        SampleTarget target = new SampleTarget();
        target.Hello("azalea");
    }
}

実に普通の呼び方。
そして、フィルタされる側のクラス定義。

[TestProxy()]
class SampleTarget : ContextBoundObject
{
     public void Hello(string p)
     {
        Console.WriteLine("Hello {0}", p);
     }
}

ContextBoundObject 噛ますのは気に入らないが、、、、まぁ勉強中。今はよしとしませう。
TestProxyAttribute の属性をつけるだけで、大きく変更はなし。

[AttributeUsage(AttributeTargets.Class)]
public class TestProxyAttribute : ProxyAttribute
{
    public TestProxyAttribute()
    { }

    public override MarshalByRefObject CreateInstance(Type serverType)
    {
        MarshalByRefObject target = base.CreateInstance(serverType) as MarshalByRefObject;
        SampleProxy proxy = new SampleProxy((SampleTarget)target, serverType);

        return (MarshalByRefObject)proxy.GetTransparentProxy();
    }
}

属性つけたクラスがインスタンスを持つタイミングで、CreateInstance 経由でプログシが乗っかると。
で、肝心のプロクシが

class SampleProxy : RealProxy
{
    private readonly SampleTarget _target;

    public SampleProxy(SampleTarget target, Type t)
        : base(t)
    {
        this._target = target;
    }

    public override System.Runtime.Remoting.Messaging.IMessage
        Invoke(System.Runtime.Remoting.Messaging.IMessage msg)
    {
        IMessage response = null;
        IMethodCallMessage call = (IMethodCallMessage)msg;
        IConstructionCallMessage ctor = call as IConstructionCallMessage;

        if (ctor != null)
        {
            // インスタンスが null なら作るんだけど
            RealProxy sp = RemotingServices.GetRealProxy(this._target);
            sp.InitializeServerObject(ctor);
            MarshalByRefObject tp = this.GetTransparentProxy() as MarshalByRefObject;
            response = EnterpriseServicesHelper.CreateConstructionReturnMessage(ctor, tp);
        }
        else
        {
            response = this.Invoke(call);
        }

        return response;
    }

    private IMessage Invoke(IMethodCallMessage call)
    {
        ReturnMessage response;

        Console.WriteLine("Loggong....start");
        response = RemotingServices.ExecuteMessage(this._target, call) as ReturnMessage;
        Console.WriteLine("Loggong....end");

        return response;
    }
}

うーん。Attribute の引数、プロパティに型情報乗っけて、、、、って Genericでは透過的に RealProxy には乗らんか、、、
横取り対象クラスが MarshalByRefObject でないと Attribute も ExecuteMessage かからないのがどうにかならんか、、、、。

*1:蛇足だが、Javaコンパイラに仕掛けて、別名コンパイルしてラップを噛ませてるので気に入らない