使用代理拦截 C# 中的方法调用 [英] Intercepting method calls in C# using Proxies

查看:30
本文介绍了使用代理拦截 C# 中的方法调用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

What I'm trying to do is to be able to intercept calls to an object's methods and properties for cross-cutting concerns. I'm using proxy-based AOP using ContextBoundObject.

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

However this doesn't work for recursive method calls, The first call against the target will be intercepted by the proxy and successfully invoked, allowing me to do cross-cut here. However subsequent method calls from within the first method will stay within the target class and are not intercepted by the proxy as if no marshaling occurs!

有什么办法可以让它工作吗?(我试图避免使用 PostSharp、Unity 或 Spring.Net 等第三方库)

Is there any way I can make it work? (I'm trying to avoid third-party libraries like PostSharp, Unity or 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,没有简单的方法可以在不使用代理和封送处理的情况下拦截方法调用,它们都有自己的缺点!如果有人想拦截方法/属性调用(例如横切关注点),我发现 RealProxy 会有所帮助.

C# designers have never been in favor of AOP, there's no easy way to intercept method calls without using Proxies and Marshaling, which have their own drawbacks! In case anyone wants to intercept method/property calls (eg. cross cutting concerns), I've found RealProxy to be of some help.

来自 MSDN 的 RealProxy:

RealProxy From MSDN:

跨任何远程处理边界使用对象的客户端是实际上使用对象的透明代理.透明的代理提供了实际对象驻留在客户的空间.它通过将对其发出的呼叫转发到使用远程基础设施的真实对象.

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 代理的类型必须是 接口 或从 MarshalByRefObject 继承.

Note: A type being proxied using RealProxy must be either an interface or inherit from MarshalByRefObject.

以下是使用工厂方法在运行时创建对象代理的 RealProxy 的一些实现:

Here's some implementation of RealProxy using a Factory Method to create a proxy of an object at runtime:

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 作为工厂来创建对象的代理并拦截所有方法/属性调用并调用实际方法.

You can use the RuntimeProxy as a factory to create a proxy of an object and intercept all method/property calls and invoke the actual method.

这是一个示例:

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 类的实例创建代理并拦截调用:

Use RuntimeProxy class to create a proxy for an instance of the SomeClass class and intercept the calls:

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

You could use interface types in case you don't want to inherit from MarshalByRefObject class.

这篇关于使用代理拦截 C# 中的方法调用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆