Castle.DynamicProxy Part 1: ClassProxy

1.Castle中代理对象的分类

总的来说,代理对象大概可以分为2大类:

1.继承类型的代理对象

一类是继承类型的代理类。即:有一个类A,它的代理类是B。B是继承自A的。调用代理类B中的方法时,可以通过拦截器做一些逻辑判断等等。但注意,A中的类只能是virtual的才可以被代理。C#中只有virtual方法才能被override。

这种代理类在castle的动态代理中只有一种,就是ClassProsy。也是最简单,最容易理解的一种代理类。

2.组合类型的代理对象

还有一类是组合型的代理类。即有一个类A,它的代理类是B。B不是继承自A的,而是B中包含了一个A的实例。这种代理就没有限制A中的方法必须是virtual的。

这种代理类型在castle的动态代理中有如下几种:

  • class proxy with target
  • interface proxy without target
  • interface proxy with target
  • interface proxy with target interface

这几种代理类将在后续的文章中进行分别说明。

2.一个简单的classProxy例子

准备一个汽车类,这个类在后面将会被代理

创建一个简单的汽车类:为保持这个demo的简单,该类现在只有2个虚方法:Start和Stop。

public class Car

    {public virtual void Start()

        {

            Console.WriteLine("启动");

        }



        public virtual void Stop()

        {

            Console.WriteLine("停止");

        }

    }

创建一个拦截器

创建一个简单的拦截器,这个拦截器只是很简单的在方法调用前和调用后打印一句话。

public class SimpleInterceptor : Castle.Core.Interceptor.IInterceptor

    {

        public void Intercept(IInvocation invocation)

        {

            Console.WriteLine("begin invoke");

            invocation.Proceed();

            Console.WriteLine("end invoke");

        }

    }

创建Car类的一个代理

使用ProxyGenerator的CreateClassProxy方法来创建一个代理。然后调用代理类的start和end方法。

public class ClassProxyDemo

    {

        public void Main()

        {

            SimpleInterceptor interceptor = new SimpleInterceptor();



            //给Car类生成代理   

            ProxyGenerator generator = new ProxyGenerator();

            var car = generator.CreateClassProxy<Car>(new IInterceptor[]{interceptor});

            car.Start();

            car.Stop();

        }

    }

可以想象一下,在调用start和end方法之前应该都会在控制台中打印出相应内容。以下是输出结果,和我们的预想一致。

------ Test started: Assembly: Castle.DynamicProxy.Demo.dll ------



begin invoke

启动

end invoke

begin invoke

停止

end invoke



1 passed, 0 failed, 0 skipped, took 0.21 seconds (Ad hoc).



将类的方法修改为非virtual

如果将Car类的Stop方法改成非虚类,结果会是怎么样呢?

public void Stop()

        {

            Console.WriteLine("停止");

        }

输出的日志为:

------ Test started: Assembly: Castle.DynamicProxy.Demo.dll ------



begin invoke

启动

end invoke

停止



1 passed, 0 failed, 0 skipped, took 0.21 seconds (Ad hoc).



可以看到,只有start方法被拦截到了。stop方法没有没拦截到。

在使用ClassProxy代理时,只有类的vierual方法,或者virtual属性才可以被代理。

 

3.DynamicProxy中的拦截器

首先看下Interceptor借口的定义吧:

#region Assembly Castle.Core.dll, v1.1.0.0

#endregion

namespace Castle.Core.Interceptor

{

    // Summary:

    //     New interface that is going to be used by DynamicProxy 2

    public interface IInterceptor

    {

        void Intercept(IInvocation invocation);

    }

}

IInvocation接口封装了对当前方法调用的所有信息。它的成员如下所示:

下面让我们来对拦截器做一些改进:

改进1:获取拦截方法的名称

我们希望拦截器中可以告诉我们当前是哪个方法被调用了,这个很简单,直接调用invocatiion.method的name属性即可。

var metodInfo = invocation.Method;

     string methodName = metodInfo.Name;

改进2:让拦截器有限速功能

car是可以加速的,但是如果car的速度过高,我们就不允许加速了。为了实现这个功能,我们需要在car类中添加一个字段和属性来表述当前速度。

private int _speed;

    public virtual int Speed { get { return _speed; } }

在拦截器中进行判断,如果调用的是Accelerate方法,就判断,加速后的速度是否超速(>60)。如果不,允许加速。否则,不执行加速。

public class SimpleInterceptor : IInterceptor

    {

        public void Intercept(IInvocation invocation)

        {

            var metodInfo = invocation.Method;

            string methodName = metodInfo.Name;



            var car = invocation.InvocationTarget as Car;

            if(invocation.Arguments.Any())

            {

                int speed = (int)invocation.Arguments[0];

                

                if (methodName == "Accelerate" && car.Speed + speed > 60)

                {

                    Console.WriteLine("speed is too high...");

                    return;

                }

            }

            

invocation.Proceed();}

    }

如果被代理类中的属性是virtual的,也可以被拦截。拦截的方法名是get_属性名。如访问代理类的Speed属性,被拦截的方法是:get_Speed。

可以使用invocation.InvocationTarget as Car来访问代理类的实例。这样可以访问代理类中的属性等。在本例中是访问代理类的Speed属性。

 

4.最终代码

using System;

using System.Linq;

using Castle.Core.Interceptor;

using Castle.DynamicProxy;





namespace DotNetDemos.Castle.DynamicProxy2

{

    public class ClassProxyDemo

    {

        public void Main()

        {

            SimpleInterceptor interceptor = new SimpleInterceptor();



            //给?Car类à生ú成é代ú理í   

            ProxyGenerator generator = new ProxyGenerator();

            var car = generator.CreateClassProxy<Car>(new IInterceptor[] { interceptor });

            //car.Start();

            //car.Stop();

            //car.Accelerate(40);

            //car.Accelerate(40);

            int speed= car.GetSpeed();

            Console.WriteLine(speed);

        }

    }



    public class SimpleInterceptor : IInterceptor

    {

        public void Intercept(IInvocation invocation)

        {

            var metodInfo = invocation.Method;

            string methodName = metodInfo.Name;



            var car = invocation.InvocationTarget as Car;

            if(invocation.Arguments.Any())

            {

                int speed = (int)invocation.Arguments[0];

                

                if (methodName == "Accelerate" && car.Speed + speed > 60)

                {

                    Console.WriteLine("speed is too high...");

                    return;

                }

            }



            Console.WriteLine("begin invoke {0} ",methodName);

            

            if (methodName == "GetSpeed")

            {

                invocation.ReturnValue = 200;

            }

            else

            {

                invocation.Proceed();

            }

            Console.WriteLine("end invoke {0} ",methodName);

        }

    }



    public class Car

    {

        private int _speed;



        public int Speed { get { return _speed; } }



        public virtual void Start()

        {

            Console.WriteLine("启动");

        }



        public virtual void Stop()

        {

            Console.WriteLine("停止");

        }



        public virtual void Accelerate(int speed)

        {

            _speed += speed;

            Console.WriteLine("加速: +{0}",speed);

        }



        public virtual void Decelerate(int speed)

        {

            _speed -= speed;

            Console.WriteLine("减速: -{0}",speed);

        }



        public virtual int GetSpeed()

        {

            return _speed;

        }

    }





}

 

5.总结

本篇中介绍的ClassProxy是Castle的代理对象中唯一的一个继承类型的代理,这种代理要求被代理类的方法和属性必须是virtual的,侵入性比较强。和基于组合的代理对象相比,这种ClassProxy是相对比较简单的。接下来,会介绍Castel中的基于组合的代理对象,都是一些接口代理。功能更强大,在实际场景中也使用的更多。

关于ClassProxy在实际项目中的使用场景,目前还不太清楚。。待研究。NHibernate的实体类映射时,实体的属性都要求是virtual的,应该是使用的ClassProxy。

你可能感兴趣的:(dynamic)