素の .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 かからないのがどうにかならんか、、、、。