为什么DynamicProxy的拦截器不会对每个虚方法调用都被调用?

12
一个例子能最好地解释它:
public interface IA { 
  void foo();
  void bar();
}

public class A : IA {
  public virtual void foo(){
    Console.Write("foo");
    bar();                  //call virtual method
  }
  public virtual void bar(){
    Console.Write("bar");
  }
}

public class Interceptor : IInterceptor {
  public void Intercept(IInvocation invocation)
  {
    Console.WriteLine("Intercepted: " + invocation.Method.Name);
    invocation.Proceed();
  }
}

Main(){
  IA a = new A();

      //proxy-ing an interface, given an implementation
  IA proxy = new Castle.DynamicProxy.ProxyGenerator()
                 .CreateInterfaceProxyWithTarget(a, new Interceptor());
  proxy.foo();

}

我本以为输出结果会是:

Intercepted foo
foo
Intercepted bar
bar

相反,我得到:
Intercepted foo
foo
bar

为什么?

动态代理是如何工作的? 我原以为生成的代理会从被代理的类中继承,但似乎它使用组合将被代理接口中的每个方法委托给实际实现。

我已经尝试过Castle DynamicProxy和一个较旧的动态代理实现,来自Cramon

2个回答

14

看起来我的猜测是正确的。

我尝试了同样的例子,只是这一次直接从类类型创建代理:

Main(){

  //proxy-ing an explicit type
  A proxy = (A) new Castle.DynamicProxy.ProxyGenerator()
                 .CreateClassProxy<A>(new Interceptor());
  proxy.foo();

}

结果正是我一开始所期望的:

Intercepted foo
foo
Intercepted bar
bar

这让我得出以下结论:

  • 从接口创建代理时,它使用组合将调用委托给实现。
  • 从(类)类型创建代理时,它会继承该类型,因此类类型中的所有虚拟调用都将调用代理中的重写方法。

当使用接口实现创建接口代理时,生成的代理看起来像这样:

class InterfaceProxy: IA { //implements interface
  IA m_impl;
  [...]

  Proxy(IA i_impl){
    m_impl = i_impl;
  }
  public void foo(){
    //overly-simplified, but you get the picture
    InvokeInterceptors("foo");

    //execution gets here when calling 'invocation.Proceed()' 
    //from the interceptor

    m_impl.foo();  //pass the execution to the implementation; 
                   //the proxy has no more control over what gets executed.

  }
  public void bar(){
    InvokeInterceptors("bar");
    m_impl.bar();
  }
}

创建类代理时,代码看起来像这样:

class ClassProxy: A { //inherits class type

  Proxy(): base() { ... }

  public override void foo(){
    InvokeInterceptors("foo");

    //execution gets here when calling 'invocation.Proceed()' 
    //from the interceptor

    base.foo();  //pass the execution to the base class 

  }
  public void bar(){
    InvokeInterceptors("bar");
    base.bar();
  }
}

哇,你是Castle DynamicProxy宇宙中的摇滚明星 :) 感谢编写教程!(或者我应该说, THE 教程 ;) - Cristian Diaconescu

8
你正在使用的是 CreateInterfaceProxyWithTarget 方法,它指示代理生成器创建一个接口代理并将调用转发到目标对象,所以你看到的就是你要求它做的事情。
如果你想让代理从你的类派生出来,那么你需要使用 CreateClassProxy 方法。

当我看到你的答案时,我刚刚完成了自己的回答 :) 所以看起来我的推理是正确的。 - Cristian Diaconescu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接