2016-08-18 31 views
6
namespace DynamicInterception 
{ 
    public class Calculator 
    { 
     public virtual int Div(int a, int b) 
     { 
      try 
      { 
       return a/b; 
      } 
      catch (Exception ex) 
      { 
       Console.WriteLine(ex.Message.ToString()); 
       return 0; 
      } 
     } 
    } 

    [Serializable] 
    public abstract class Interceptor : IInterceptor 
    { 
     public void Intercept(IInvocation invocation) 
     { 
      ExecuteBefore(invocation); 
      invocation.Proceed(); 
      ExecuteAfter(invocation); 
     } 
     protected abstract void ExecuteAfter(IInvocation invocation); 
     protected abstract void ExecuteBefore(IInvocation invocation); 
    } 

    public class CalculatorInterceptor : Interceptor 
    { 
     protected override void ExecuteBefore(Castle.DynamicProxy.IInvocation invocation) 
     { 
      Console.WriteLine("Start: {0}", invocation.Method.Name); 
     } 

     protected override void ExecuteAfter(Castle.DynamicProxy.IInvocation invocation) 
     { 
      Console.WriteLine("End: {0}", invocation.Method.Name); 
     } 
    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      ProxyGenerator generator = new ProxyGenerator(); 
      Calculator c = generator.CreateClassProxy<Calculator>(new CalculatorInterceptor()); 
      var r = c.Div(11, 0); 
      Console.ReadKey(); 
     } 
    } 
} 

Czy to możliwe, aby zastąpić public virtual int Div(int a,int b) z interfejsemZamek Dynamiczna Pełnomocnik interfejsie oraz niepochodzące Klasa

interface ICalculator 
{ 
    int Div(int a, int b); 
} 

Jak więc powinna wyglądać deklaracji proxy?

ProxyGenerator generator = new ProxyGenerator(); 
Calculator c = generator.CreateClassProxy<Calculator>(new CalculatorInterceptor()); 

Odpowiedz

5

Jeśli chcesz dodać interfejs do Calculator i wykonać te 2 linie to będzie działać tak samo:

public interface ICalculator 
{ 
    int Div(int a, int b); 
} 

public class Calculator : ICalculator 
{ 

    public int Div(int a, int b) 
    { 
     try 
     { 
      return a/b; 
     } 
     catch (Exception ex) 
     { 
      Console.WriteLine(ex.Message.ToString()); 
      return 0; 
     } 
    } 
} 

ProxyGenerator generator = new ProxyGenerator(); 
Calculator c = generator.CreateClassProxy<Calculator>(new CalculatorInterceptor()); 

Ale tak naprawdę nie robić nic przez to - jesteś wciąż tworząc proxy dla konkretnego typu pochodnego. Zakładam, że chcesz coś takiego, jak "CreateClassProxy<ICalculator>". To nie zadziała, ponieważ CreateClassProxy ma ogólne ograniczenie na where TClass : class.

To, co możesz zrobić, to różne metody, które możesz wypróbować. Ale wciąż naiwny wykonanie jak poniżej nie będzie działać:

ICalculator c = generator.CreateInterfaceProxyWithoutTarget<ICalculator>(new CalculatorInterceptor()); 
c.Div(1, 2); 

Będzie to wykonać, należy zadzwonić do przechwytywania i zawiedzie podczas uruchamiania invocation.Proceed(); z błędem:

System.NotImplementedException This is a DynamicProxy2 error: The interceptor attempted to 'Proceed' for method 'Int32 Div(Int32, Int32)' which has no target. When calling method without target there is no implementation to 'proceed' to and it is the responsibility of the interceptor to mimic the implementation (set return value, out arguments etc)

Tak jak dobry charakter orientacyjny (poważnie) błędy określone przez Castle - musisz jakoś wdrożyć do tego - lub przez wskazanie go w przechwytującym - przez zarejestrowanie dla tego interfejsu Component.

Zamiast tego można zrobić tak: (Sprawdź komentarze w kodzie)

ProxyGenerator generator = new ProxyGenerator(); 

ICalculator calculator = new Calculator(); 
var proxyCalculator = generator.CreateInterfaceProxyWithTarget(typeof(ICalculator),calculator, new CalculatorInterceptor()); 

calculator.Div(1, 2); // Will execute but will not be intercepted 
((ICalculator)proxyCalculator).Div(11, 0); //Will execute and will be intercepted 

Ale po mówiąc, wszystko, co powiedziano wyżej, jeżeli cel za to wszystko ma mieć przechwytywania przechwycić metody następnie tylko "stary" rejestruje się w kontenerze:

WindsorContainer container = new WindsorContainer(); 
container.Register(
    Component.For<CalculatorInterceptor>(), 
    Component.For<ICalculator>() 
      .ImplementedBy<Calculator>() 
      .Interceptors<CalculatorInterceptor>()); 

var calculator = container.Resolve<ICalculator>(); 
calculator.Div(1, 0); 

// Output: 
// Start: Div 
// Attempted to divide by zero 
// End: Div