gpt4 book ai didi

c# - 使用代理在 C# 中拦截方法调用

转载 作者:太空狗 更新时间:2023-10-30 01:22:23 25 4
gpt4 key购买 nike

我正在尝试做的是能够拦截对对象方法和属性的调用以实现横切关注点。我使用 ContextBoundObject 使用基于代理的 AOP。

但是这对递归方法调用不起作用,针对目标的第一次调用将被代理拦截并成功调用,允许我在这里进行横切。但是,第一个方法中的后续方法调用将保留在目标类中,并且不会被代理拦截,就好像没有发生编码(marshal)处理一样!

有什么方法可以让它发挥作用吗? (我试图避免第三方库,如 PostSharp、Unity 或 Spring.Net)

class Program
{
static void Main(string[] args)
{
var t = new SimpleObject();
t.TestMethod1();
}
}


[Intercept]
class SimpleObject : ContextBoundObject
{
public string TestMethod1()
{
return TestMethod2();
}

public string TestMethod2()
{
return "test";
}
}

[AttributeUsage(AttributeTargets.Class)]
public class InterceptAttribute : ContextAttribute, IContributeObjectSink
{
public InterceptAttribute()
: base("Intercept")
{ }

public override bool IsContextOK(Context ctx, IConstructionCallMessage ctorMsg)
{
return false;
}

public IMessageSink GetObjectSink(MarshalByRefObject obj, IMessageSink nextSink)
{
return new InterceptSink(nextSink);
}
}


public class InterceptSink : IMessageSink
{
public IMessageSink NextSink { get; private set; }

public InterceptSink(IMessageSink nextSink)
{
this.NextSink = nextSink;
}

public IMessage SyncProcessMessage(IMessage msg)
{
IMethodCallMessage mcm = (msg as IMethodCallMessage);

// { cross-cut here }

IMessage rtnMsg = this.NextSink.SyncProcessMessage(msg);
IMethodReturnMessage mrm = (rtnMsg as IMethodReturnMessage);

// { cross-cut here }

return mrm;
}

public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)
{
return null;
}
}

最佳答案

C# 设计者从来都不赞成 AOP,如果不使用代理和编码(marshal)处理,就没有简单的方法来拦截方法调用,它们都有自己的缺点!如果有人想拦截方法/属性调用(例如,横切关注点),我发现 RealProxy 会有所帮助。

来自 MSDN 的 RealProxy:

A client that uses an object across any kind of a remoting boundary is actually using a transparent proxy for the object. The transparent proxy provides the illusion that the actual object resides in the client's space. It achieves this by forwarding calls made on it to the real object using the remoting infrastructure.

注意:使用RealProxy 代理的类型必须是接口(interface) 或继承自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; }
}

使用 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 类继承,您可以使用接口(interface)类型。

关于c# - 使用代理在 C# 中拦截方法调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13659185/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com