How to use Castle.DynamicProxy-2.1, part 2

      Preview

      2.2 Demo

      2.2.1 Common scenario

      Interface & Implement class:

Code
 
1    public interface IRotateable
 
2    {
 
3        int Degree { get; }
 
4        void Rotate(int anglePoint);
 
5        void AntiRotate(int anglePoint);
 
6    }
 
7
 
8    public class Map : IRotateable
 
9    {
10        private int degree;
11
12        private string name;
13
14        public Map()
15        {
16            degree = 0;
17            name = "Default";
18        }
19
20        public Map(int degreePar)
21        {
22            degree = degreePar;
23        }
24
25        public Map(string namePar)
26        {
27            degree = 0;
28            name = namePar;
29        }
30
31        public Map(string namePar, int degreePar)
32        {
33            name = namePar;
34            degree = degreePar;
35        }
36
37        public string Name
38        {
39            get
40            {
41                return name;
42            }
43            set
44            {
45                name = value;
46            }
47        }
48
49        IRotateable Members#region IRotateable Members
50
51        public int Degree
52        {
53            get { return degree; }
54        }
55
56        public void Rotate(int anglePoint)
57        {
58            degree = degree + anglePoint;
59            Console.WriteLine(name + "@Rotate ");
60            if (degree > Common.MaxDegree)
61            {
62                Console.WriteLine("A circle happened");
63                degree = degree % Common.MaxDegree;
64            }
65        }
66
67        #endregion
68
69        IRotateable Members#region IRotateable Members
70
71
72        public void AntiRotate(int anglePoint)
73        {
74            degree = degree - anglePoint;
75            Console.WriteLine(name + "@Antirotate ");
76            if (degree < Common.MinDegree)
77            {
78                Console.WriteLine("A circle happened");
79                degree = degree + Common.MaxDegree;
80            }
81        }
82
83        #endregion
84    }

      如果我们是创建接口代理, 那么接口一定要是public. 实现类理论情况下可以不为public, 我在使用CreateInterfaceProxyWithTargetInterface创建proxy的时候, 实现类确实可以不为public, 但使用CreateInterfaceProxyWithTarget时实现类却必须是public.

      MulitInterpceptor:

    class LoggingInterceptor : IInterceptor
    {

        
#region IInterceptor Members

        
public void Intercept(IInvocation invocation)
        {
            Console.WriteLine(
"Logging, before execute " + invocation.Method);
            invocation.Proceed();
            Console.WriteLine(
"Logging, after execute " + invocation.Method);
        }

        
#endregion
    }

    
class VerifingIntercptor : IInterceptor
    {

        
#region IInterceptor Members

        
public void Intercept(IInvocation invocation)
        {
            Console.WriteLine(
"Verifing, before execute " + invocation.Method);
            invocation.Proceed();
            Console.WriteLine(
"Verifing, after execute " + invocation.Method);
            
//foreach (IInterceptor ior in (invocation.Proxy as IProxyTargetAccessor).GetInterceptors())
            
//{
            
//    Console.WriteLine(ior);
            
//}
        }

        
#endregion
    }

    
class exchangingInterceptor : IInterceptor
    {
        
private Map reservedMap;

        
public exchangingInterceptor()
        {
            reservedMap 
= new Map();
        }

        
public exchangingInterceptor(Map mapParameter)
        {
            reservedMap 
= mapParameter;
        }

        
#region IInterceptor Members

        
public void Intercept(IInvocation invocation)
        {
            Console.WriteLine(
"Exchanging, before execute " + invocation.Method);
            ChangeToOthers(invocation);
            invocation.Proceed();
            Console.WriteLine(
"Exchanging, after execute " + invocation.Method);
        }

        
private void ChangeToOthers(IInvocation invocation)
        {
            var changeProxyTarget 
= invocation as IChangeProxyTarget;
            changeProxyTarget.ChangeInvocationTarget(reservedMap);
        }

        
#endregion
    }

   

     ProxyGeneratorHook:

 1    class MainProxyHook : IProxyGenerationHook
 
2    {
 
3
 
4        #region IProxyGenerationHook Members
 
5
 
6        public void MethodsInspected()
 
7        {
 
8            Console.WriteLine("Everything is setupped well @ code generation time");
 
9        }
10
11        public void NonVirtualMemberNotification(Type type, System.Reflection.MemberInfo memberInfo)
12        {
13            Console.WriteLine("NoVirtualMethod - " + memberInfo.Name + " is not be hooked @ code generation time");
14        }
15
16        public bool ShouldInterceptMethod(Type type, System.Reflection.MethodInfo methodInfo)
17        {
18            //if (methodInfo.Name.StartsWith("get_"))
19            //{
20            //    Console.WriteLine("We do not allow method - " + methodInfo.Name + " be hooked @ code generation time");
21            //    return false;
22            //}
23            Console.WriteLine("Method - " + methodInfo.Name + " is hooked @ code generation time ");
24            return true;
25        }
26
27        #endregion
28    }

      注意其中注释掉的地方. Intercept本质是通过子类化并重写, 所以在代理类生成期间执行的ShouldInterceptMethod()决定了哪些方法会被重写(它会遍历type中的所有虚方法, 并通过返回值True/False决定是否override). 由于我们例子中是接口代理, 所以代理类必须实现接口的所有方法, 我们这里不能选择解除某些方法的拦截(但在非接口代理对象中可以这么做).

      IInterceptSelector:

 1    class PairInterceptorSelector : IInterceptorSelector
 
2    {
 
3
 
4        #region IInterceptorSelector Members
 
5
 
6        public IInterceptor[] SelectInterceptors(Type type, System.Reflection.MethodInfo method, IInterceptor[] interceptors)
 
7        {
 
8            if (method.Name.Equals("AntiRotate"&& interceptors.Any(q => q is exchangingInterceptor))
 
9            {
10                Console.WriteLine("We do not allow method - " + method.Name + " be intercepted by Type - exChangeingInterceptor");
11                return interceptors.Where(q => !(q is exchangingInterceptor)).ToArray();
12            }
13            return interceptors;
14        }
15
16        #endregion
17    }

      我们制定在每次方法调用的时候的匹配规则: 如果调用的是方法AntiRotate()并且代理对象中拥有类型为exChangingInterceptor的拦截器, 我们就卸载这个拦截器, 但保留其他的拦截器; 在调用其他方法的情况下, 保留所有拦截器.

      下面是Main方法:

 1        static void Main(string[] args)
 
2        {
 
3            Map mapOne = new Map("One");
 
4            Map mapTwo = new Map("Two");
 
5
 
6            IList<IInterceptor> allInterceptorGroup = new List<IInterceptor>()
 
7            {
 
8                new LoggingInterceptor(),
 
9                new exchangingInterceptor(mapTwo),
10                new VerifingIntercptor()
11            };
12            IList<IInterceptor> noExchangingInterceptorGroup = new List<IInterceptor>()
13            {
14                new LoggingInterceptor(),
15                new VerifingIntercptor()
16            };
17
18            Console.WriteLine("@Code generating time:");
19            ProxyGenerator pg = new ProxyGenerator();
20            var options = new ProxyGenerationOptions(new MainProxyHook()) { Selector = new PairInterceptorSelector() };
21           object proxy = pg.CreateInterfaceProxyWithTarget(typeof(IRotateable), mapOne, options, noExchangingInterceptorGroup.ToArray());                     
22
23            Console.WriteLine("@Object Running time:");
24            (proxy as IRotateable).Rotate(10);
25            (proxy as IRotateable).AntiRotate(10);
26
27        }

      其输出:

      @Code generating time:
      Method - Rotate is hooked @ code generation time
      Method - AntiRotate is hooked @ code generation time
      Method - get_Degree is hooked @ code generation time
      Everything is setupped well @ code generation time
      @Object Running time:
      Logging, before execute Void Rotate(Int32)
      Verifing, before execute Void Rotate(Int32)
      One@Rotate
      Verifing, after execute Void Rotate(Int32)
      Logging, after execute Void Rotate(Int32)
      Logging, before execute Void AntiRotate(Int32)
      Verifing, before execute Void AntiRotate(Int32)
      One@Antirotate
      Verifing, after execute Void AntiRotate(Int32)
      Logging, after execute Void AntiRotate(Int32)

      

      2.2.2 Using exchange

      Main方法:

 1        static void Main(string[] args)
 
2        {
 
3            Map mapOne = new Map("One");
 
4            Map mapTwo = new Map("Two");
 
5
 
6            IList<IInterceptor> allInterceptorGroup = new List<IInterceptor>()
 
7            {
 
8                new LoggingInterceptor(),
 
9                new exchangingInterceptor(mapTwo),
10                new VerifingIntercptor()
11            };
12            IList<IInterceptor> noExchangingInterceptorGroup = new List<IInterceptor>()
13            {
14                new LoggingInterceptor(),
15                new VerifingIntercptor()
16            };
17
18            Console.WriteLine("@Code generating time:");
19            ProxyGenerator pg = new ProxyGenerator();
20            var options = new ProxyGenerationOptions(new MainProxyHook()) { Selector = new PairInterceptorSelector() };        
21            object proxy = pg.CreateInterfaceProxyWithTargetInterface(typeof(IRotateable), mapOne, options, allInterceptorGroup.ToArray());
22            
23            Console.WriteLine("@Object Running time:");
24            (proxy as IRotateable).Rotate(10);
25            (proxy as IRotateable).AntiRotate(10);
26
27        }

      输出:

      @Code generating time:
      Method - Rotate is hooked @ code generation time 
      Method - AntiRotate is hooked @ code generation time 
      Method - get_Degree is hooked @ code generation time 
      Everything is setupped well @ code generation time
      @Object Running time:
      Logging, before execute Void Rotate(Int32)
      Exchanging, before execute Void Rotate(Int32)
      Verifing, before execute Void Rotate(Int32)
      Two@Rotate 
      Verifing, after execute Void Rotate(Int32)
      Exchanging, after execute Void Rotate(Int32)
      Logging, after execute Void Rotate(Int32)
      We do not allow method - AntiRotate be intercepted by Type - exChangeingInterceptor
      Logging, before execute Void AntiRotate(Int32)
      Verifing, before execute Void AntiRotate(Int32)
      One@Antirotate 
      A circle happened
      Verifing, after execute Void AntiRotate(Int32)
      Logging, after execute Void AntiRotate(Int32)

      由于Rotate发生在另一个Map对象上, 所以当我们执行AntiRotate的时候(此方法停用exchangingInterceptor), 这时的map仍然为0度, 就出现了'A circle happened'.

 

      2.2.3 Using Minix

      Minix使用混合接口, 可以让我们以更优雅的方式给原始对象添加行为.

      Interface & Implement class:

 1    public interface IZoom
 
2    {
 
3        void ZoomOut();
 
4        void ZoomIn();
 
5    }
 
6
 
7    class ZoomImp : IZoom
 
8    {
 
9        private Map map;
10
11        public ZoomImp()
12        {
13            map = new Map();
14        }
15
16        public ZoomImp(Map mapInit)
17        {
18            map = mapInit;
19        }
20
21        #region IZoom Members
22
23        public void ZoomOut()
24        {
25            Console.WriteLine("ZoomOut @" + map.Name);
26        }
27
28        public void ZoomIn()
29        {
30            Console.WriteLine("ZoomIn @" + map.Name);
31        }
32
33        #endregion
34    }

      Main:   

 1        static void Main(string[] args)
 
2        {
 
3            Map mapOne = new Map("One");
 
4            Map mapTwo = new Map("Two");
 
5
 
6            IList<IInterceptor> allInterceptorGroup = new List<IInterceptor>()
 
7            {
 
8                new LoggingInterceptor(),
 
9                new exchangingInterceptor(mapTwo),
10                new VerifingIntercptor()
11            };
12            IList<IInterceptor> noExchangingInterceptorGroup = new List<IInterceptor>()
13            {
14                new LoggingInterceptor(),
15                new VerifingIntercptor()
16            };
17
18            Console.WriteLine("@Code generating time:");
19            ProxyGenerator pg = new ProxyGenerator();
20            var options = new ProxyGenerationOptions(new MainProxyHook()) { Selector = new PairInterceptorSelector() };
21
22            IZoom zoomImp = new ZoomImp(mapOne);
23            options.AddMixinInstance(zoomImp);
24
25           object proxy = pg.CreateInterfaceProxyWithTarget(typeof(IRotateable), mapOne, options, noExchangingInterceptorGroup.ToArray());
26                     
27            Console.WriteLine("@Object Running time:");
28            (proxy as IRotateable).Rotate(10);
29            (proxy as IRotateable).AntiRotate(10);
30            (proxy as IZoom).ZoomOut();
31        }

      输出:

      @Code generating time:
      Method - Rotate is hooked @ code generation time 
      Method - AntiRotate is hooked @ code generation time 
      Method - get_Degree is hooked @ code generation time 
      Method - ZoomOut is hooked @ code generation time 
      Method - ZoomIn is hooked @ code generation time 
      Everything is setupped well @ code generation time
      @Object Running time:
      Logging, before execute Void Rotate(Int32)
      Verifing, before execute Void Rotate(Int32)      
      One@Rotate 
      Verifing, after execute Void Rotate(Int32)
      Logging, after execute Void Rotate(Int32)
      Logging, before execute Void AntiRotate(Int32)
      Verifing, before execute Void AntiRotate(Int32)
      One@Antirotate 
      Verifing, after execute Void AntiRotate(Int32)
      Logging, after execute Void AntiRotate(Int32)
      Logging, before execute Void ZoomOut()
      Verifing, before execute Void ZoomOut()
      ZoomOut @One
      Verifing, after execute Void ZoomOut()
      Logging, after execute Void ZoomOut()

       注意, 例子中我们是使用的CreateInterfaceProxyWithTarget, 并给proxy附加了一个额外的接口实现对象, target中并没有额外接口方法的实现. 如果我们在target中定义和附加接口同样的签名方法(即便非虚), 那么在proxy上调用额外接口某方法时会执行target中定义的部分, 而不是接口实现对象类中的定义. 如果我们使用CreateInterfaceProxyWithTargetInterface, 安装一个转换拦截器(exchangingInterceptor), 并调用额外接口方法时, 这要求转换后的对象同样实现了该接口, 否则会出现类型转换错误.

      这是因为在执行CreateProxyXXX的时候, 代码生成的类型会依次在target对象类型(Minix implementor)中查找所有interfaceToProxy(Minix interface)声明的方法的定义, 并对每一个方法创建对应的Invocation, invocation中指定各种属性, 比如target(指向方法实现的Assembly.MethodInfo). 在执行完CreateProxyXXX后, 代理派生类将定位所有接口的实现方法. 并存入Dictionary<string, IInvocation>中, 比如{"IRotateable.Rotate", IProxyXXX.InvocationRotate_X(ConsoleApplication.Map.Rotate)}, {"IZoom.ZoomOut", IProxyXXX.InvocationZoomOut_X(Console.ZoomImp.ZoomOut)}. 当我们安装了一个转换拦截器, 并调用额外接口方法时, 这时候invocation的target指向的就是额外接口的实现类型对象(Minix implementor), 如果我们的转换对象没实现该接口, target方法当然就不可能一一对应.

 

posted @ 2009-06-29 00:25  Tyrael  阅读(1461)  评论(1)    收藏  举报