使用Proxies在C#中调用拦截方法

时间:2012-12-01 11:53:15

标签: c# .net marshalling aop

我要做的是能够拦截对象的方法和属性的调用,以解决交叉问题。我正在使用基于代理的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;
    }
}

1 个答案:

答案 0 :(得分:3)

C#设计师从未赞成过AOP,没有简单的方法来拦截方法调用而不使用Proxies和Marshaling,它们有各自的缺点! 如果有人想拦截方法/属性调用(例如交叉问题),我发现RealProxy可以提供一些帮助。

RealProxy来自MSDN:

  

在任何类型的远程边界中使用对象的客户端是   实际上为对象使用透明代理。透明的   代理提供了实际对象所在的错觉   客户的空间。它通过将对其发出的呼叫转发给它来实现这一点   使用远程基础设施的真实对象。

注意:使用RealProxy代理的类型必须是接口或从MarshalByRefObject继承。

以下是使用Factory方法在运行时创建对象代理的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类继承,可以使用接口类型。