C# の設計者が AOP を支持したことはありません。プロキシとマーシャリングを使用せずにメソッド呼び出しをインターセプトする簡単な方法はありません。プロキシとマーシャリングにはそれぞれ欠点があります。誰かがメソッド/プロパティの呼び出しを傍受したい場合 (たとえば、分野横断的な関心事) にRealProxy
役立つことがわかりました。
MSDN からの RealProxy:
あらゆる種類のリモーティング境界を越えてオブジェクトを使用するクライアントは、実際にはオブジェクトに透過プロキシを使用しています。透過プロキシは、実際のオブジェクトがクライアントの空間に存在するという錯覚をもたらします。これは、リモーティング インフラストラクチャを使用して、そのオブジェクトに対して行われた呼び出しを実際のオブジェクトに転送することによって実現されます。
注: を使用してプロキシされる型は、インターフェイスまたは継承元RealProxy
のいずれかである必要があります。MarshalByRefObject
RealProxy
ファクトリ メソッドを使用して実行時にオブジェクトのプロキシを作成する実装を次に示します。
public abstract class RuntimeProxy
{
public static readonly object Default = new object();
public static Target Create<Target>(Target instance, RuntimeProxyInterceptor interceptor) where Target : class
{
return (Target)new InternalProxy<Target>(instance, interceptor).GetTransparentProxy();
}
public static Target Create<Target>(Target instance, Func<RuntimeProxyInvoker, object> factory) where Target : class
{
return (Target)new InternalProxy<Target>(instance, new InternalRuntimeProxyInterceptor(factory)).GetTransparentProxy();
}
class InternalProxy<Target> : RealProxy where Target : class
{
readonly object Instance;
readonly RuntimeProxyInterceptor Interceptor;
public InternalProxy(Target instance, RuntimeProxyInterceptor interceptor)
: base(typeof(Target))
{
Instance = instance;
Interceptor = interceptor;
}
public override IMessage Invoke(IMessage msg)
{
var methodCall = (IMethodCallMessage)msg;
var method = (MethodInfo)methodCall.MethodBase;
try
{
var result = Interceptor.Invoke(new InternalRuntimeProxyInterceptorInvoker(Instance, method, methodCall.InArgs));
if (result == RuntimeProxy.Default)
result = method.ReturnType.IsPrimitive ? Activator.CreateInstance(method.ReturnType) : null;
return new ReturnMessage(result, null, 0, methodCall.LogicalCallContext, methodCall);
}
catch (Exception ex)
{
if (ex is TargetInvocationException && ex.InnerException != null)
return new ReturnMessage(ex.InnerException, msg as IMethodCallMessage);
return new ReturnMessage(ex, msg as IMethodCallMessage);
}
}
}
class InternalRuntimeProxyInterceptor : RuntimeProxyInterceptor
{
readonly Func<RuntimeProxyInvoker, object> Factory;
public InternalRuntimeProxyInterceptor(Func<RuntimeProxyInvoker, object> factory)
{
this.Factory = factory;
}
public override object Invoke(RuntimeProxyInvoker invoker)
{
return Factory(invoker);
}
}
class InternalRuntimeProxyInterceptorInvoker : RuntimeProxyInvoker
{
public InternalRuntimeProxyInterceptorInvoker(object target, MethodInfo method, object[] args)
: base(target, method, args)
{ }
}
}
public abstract class RuntimeProxyInterceptor
{
public virtual object Invoke(RuntimeProxyInvoker invoker)
{
return invoker.Invoke();
}
}
public abstract class RuntimeProxyInvoker
{
public readonly object Target;
public readonly MethodInfo Method;
public readonly ReadOnlyCollection<object> Arguments;
public RuntimeProxyInvoker(object target, MethodInfo method, object[] args)
{
this.Target = target;
this.Method = method;
this.Arguments = new ReadOnlyCollection<object>(args);
}
public object Invoke()
{
return Invoke(this.Target);
}
public object Invoke(object target)
{
if (target == null)
throw new ArgumentNullException("target");
try
{
return this.Method.Invoke(target, this.Arguments.ToArray());
}
catch (TargetInvocationException ex)
{
throw ex.InnerException;
}
}
}
をファクトリとして使用しRuntimeProxy
て、オブジェクトのプロキシを作成し、すべてのメソッド/プロパティ呼び出しをインターセプトして、実際のメソッドを呼び出すことができます。
サンプルは次のとおりです。
class SomeClass : MarshalByRefObject
{
public int Mul(int a, int b)
{
return a * b;
}
public void SetValue(int val)
{
this.Val = val;
}
public int Val { get; set; }
}
class を使用RuntimeProxy
して、クラスのインスタンスのプロキシを作成しSomeClass
、呼び出しをインターセプトします。
var test = new SomeClass();
var proxy = RuntimeProxy.Create(test, t =>
{
// cross-cut here
return t.Invoke(); // invoke the actual call
});
var res = proxy.Mul(3, 4); // method with return value
proxy.SetValue(2); // void method, setting some property
var val = proxy.Val; // property access
MarshalByRefObject
クラスから継承したくない場合は、インターフェイス型を使用できます。