问题 Castle Dynamic Proxy在从类中调用时不拦截方法调用


使用Castle的动态代理时,我遇到了一些(我认为)奇怪的行为。

使用以下代码:

class Program
{
    static void Main(string[] args)
    {
        var c = new InterceptedClass();
        var i = new Interceptor();

        var cp = new ProxyGenerator().CreateClassProxyWithTarget(c, i);

        cp.Method1();
        cp.Method2();

        Console.ReadLine();
    }
}

public class Interceptor : IInterceptor
{
    public void Intercept(IInvocation invocation)
    {
        Console.WriteLine(string.Format("Intercepted call to: " + invocation.Method.Name));

        invocation.Proceed();
    }
}

public class InterceptedClass
{
    public virtual void Method1()
    {
        Console.WriteLine("Called Method 1");
        Method2();
    }

    public virtual void Method2()
    {
        Console.WriteLine("Called Method 2");
    }
}

我期待获得输出:

  • 截获的电话:Method1
  • 称为方法1
  • 截获的电话:方法2
  • 称为方法2
  • 截获的电话:方法2
  • 称为方法2

但我得到的是:

  • 截获的电话:Method1
  • 称为方法1
  • 称为方法2
  • 截获的电话:方法2
  • 称为方法2

据我所知,如果调用来自类本身之外,动态代理只能代理方法调用,因为当从Program调用而不是从InterceptedClass调用时拦截了Method2。

我可以理解,当从代理类中进行调用时,它将不再通过代理,但只是想检查这是否是预期的,如果是,那么无论如何都要查看是否有所有调用被拦截而不管他们被叫到哪里?

谢谢


7545
2017-07-09 09:23


起源



答案:


编辑:tl; dr - 我刚尝试以不同的方式创建代理,如下所述,它产生你所追求的输出。我只需要改变这个:

var c = new InterceptedClass();
var i = new Interceptor();

var cp = new ProxyGenerator().CreateClassProxyWithTarget(c, i);

对此:

var i = new Interceptor();
var cp = new ProxyGenerator().CreateClassProxy<InterceptedClass>(i);

据我了解,代理生成器有效地创建了一个包装器对象。它们是两个独立的对象 - 一个是围绕另一个的包装器,在包装层中有拦截等。

很难看出它是怎么回事 可以 改变了什么实例 InterceptedClass 做了自己的方法调用:

  • DynamicProxy无法更改现有对象的类型;创建对象后,其类型是固定的
  • DynamicProxy无法更改绑定现有对象的现有调用的方式

如果你想 Method1 打电话 Method2  通过 使用当前代理创建代码的包装器,您需要告诉包装器的现有对象,作为其中的字段或方法参数。

或者,可能有一种不同的方式来创建代理开始 - 代理的一个方法  在某种意义上说是目标对象。我怀疑你可能想看看 CreateClassProxy 而不是 CreateClassProxyWithTarget  - 我怀疑你是这样的事实 供应 导致你出问题的目标对象。

你所看到的行为是否“预期”或者显然取决于你的期望 - 但它肯定是什么 一世 期待,不知道任何关于城堡动态代理:)


16
2017-07-09 09:28



是的,我有点想。因此,在其他方面,可以拦截对“cp”的调用,并且对“c”的调用不能,并且包括从“c”内调用“c” - 这绝不是令人惊讶的。暂且不说:我应该考虑在提问时使用稍微更具描述性的变量名,是吗? - George Goodchild
感谢您的更新 - 使用CreateClassProxyWithTarget的原因是在我试图解决的实际问题中,目标对象是由IoC容器创建的,然后我想用代理包装。但是这个特殊问题超出了这个问题的范围。谢谢你的帮助。 - George Goodchild
@George:你有没有办法说服IoC容器使用Castle Dynamic Proxy本身?如你所说,它与这个问题略有不同,但它可能是可行的...... - Jon Skeet
啊,现在这是个好主意。我会调查一下。 - George Goodchild


答案:


编辑:tl; dr - 我刚尝试以不同的方式创建代理,如下所述,它产生你所追求的输出。我只需要改变这个:

var c = new InterceptedClass();
var i = new Interceptor();

var cp = new ProxyGenerator().CreateClassProxyWithTarget(c, i);

对此:

var i = new Interceptor();
var cp = new ProxyGenerator().CreateClassProxy<InterceptedClass>(i);

据我了解,代理生成器有效地创建了一个包装器对象。它们是两个独立的对象 - 一个是围绕另一个的包装器,在包装层中有拦截等。

很难看出它是怎么回事 可以 改变了什么实例 InterceptedClass 做了自己的方法调用:

  • DynamicProxy无法更改现有对象的类型;创建对象后,其类型是固定的
  • DynamicProxy无法更改绑定现有对象的现有调用的方式

如果你想 Method1 打电话 Method2  通过 使用当前代理创建代码的包装器,您需要告诉包装器的现有对象,作为其中的字段或方法参数。

或者,可能有一种不同的方式来创建代理开始 - 代理的一个方法  在某种意义上说是目标对象。我怀疑你可能想看看 CreateClassProxy 而不是 CreateClassProxyWithTarget  - 我怀疑你是这样的事实 供应 导致你出问题的目标对象。

你所看到的行为是否“预期”或者显然取决于你的期望 - 但它肯定是什么 一世 期待,不知道任何关于城堡动态代理:)


16
2017-07-09 09:28



是的,我有点想。因此,在其他方面,可以拦截对“cp”的调用,并且对“c”的调用不能,并且包括从“c”内调用“c” - 这绝不是令人惊讶的。暂且不说:我应该考虑在提问时使用稍微更具描述性的变量名,是吗? - George Goodchild
感谢您的更新 - 使用CreateClassProxyWithTarget的原因是在我试图解决的实际问题中,目标对象是由IoC容器创建的,然后我想用代理包装。但是这个特殊问题超出了这个问题的范围。谢谢你的帮助。 - George Goodchild
@George:你有没有办法说服IoC容器使用Castle Dynamic Proxy本身?如你所说,它与这个问题略有不同,但它可能是可行的...... - Jon Skeet
啊,现在这是个好主意。我会调查一下。 - George Goodchild