使用代理服务器在C#拦截方法调用(Intercepting method calls in C# u

2019-08-03 10:00发布

我想要做的是能够拦截对横切关注的一个对象的方法的调用和属性。 我使用的基于代理的AOP ContextBoundObject

然而,这并不递归方法调用的工作,对目标的第一个呼叫将由代理截获并成功调用,让我在这里做横切。 然而随后的方法从第一个方法中调用将保持在目标类中,如果发生,因为没有封送处理被代理拦截

有没有什么办法可以使它发挥作用? (我试图避免第三方库像PostSharp,统一或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;
    }
}

Answer 1:

C#的设计师从来没有赞成AOP的,还有拦截方法没有简单的方法,而无需使用代理和编组调用,它有自己的缺点! 如果有人想拦截方法/属性调用(如横切关注点),我发现RealProxy是有一定的帮助。

RealProxy从MSDN:

跨任何种类的远程处理边界的使用对象的客户端实际上使用该对象的透明代理。 透明代理提供了实际对象驻留在客户机的空间的幻觉。 它通过转发对其提出使用远程基础设施的真正对象调用实现这一点。

注意:使用被代理A型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类。



文章来源: Intercepting method calls in C# using Proxies