Почему перехватчик DynamicProxy не будет вызван для * каждого * вызова виртуального метода? - PullRequest
9 голосов
/ 28 января 2010

Пример объясняет это лучше всего:

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 ]

13 голосов
/ 28 января 2010

Похоже, мое предположение было верным.

Я попробовал тот же пример, только на этот раз создаю прокси прямо из класса:

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();
  }
}
7 голосов
/ 28 января 2010

Вы используете метод CreateInterfaceProxyWithTarget, который инструктирует построителя прокси создать прокси для интерфейса и перенаправить вызовы целевому объекту, поэтому вы видите, что вы просили его сделать.

Если вы хотите, чтобы прокси был производным от вашего класса, вам нужно будет использовать метод CreateClassProxy.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...