【问题标题】:Intercepting method calls in C# using Proxies使用代理拦截 C# 中的方法调用
【发布时间】:2017-10-26 17:24:34
【问题描述】:

我想要做的是能够拦截对对象的方法和属性的调用,以解决横切关注点。我正在使用基于代理的 AOP,使用 ContextBoundObject

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

有什么办法可以让它工作吗? (我尽量避免使用 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;
    }
}

【问题讨论】:

  • 好奇你为什么要避免后期锐化?
  • 不是我关心的问题,这是公司政策!
  • 很遗憾,PostSharp 会很容易地给你这个,甚至是不需要许可证的免费方面
  • 有没有人在不使用 PostSharp 的情况下找到解决方案?我现在正面临这个问题......
  • 我最终使用System.Runtime.Remoting.Proxies.RealProxy 在运行时为对象创建代理。您可以在覆盖的Invoke 方法中拦截方法调用。它不是纯粹的 AOP,但通过一些解决方法,您也许可以模仿 AOP

标签: c# .net marshalling aop


【解决方案1】:

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; }
}

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-09
    • 1970-01-01
    • 1970-01-01
    • 2014-10-11
    • 2015-04-29
    相关资源
    最近更新 更多