C#之AOP初探

来源:互联网 发布:淘宝助理怎么弄 编辑:程序博客网 时间:2024/06/08 15:39

一、前言

看了使用AOP 使C#代码更清晰,很受启发。但版主似乎已经放弃C#了,所以他的这篇文章让人看得似懂非懂。但文中一段话说的特好:

有许多AOP类库通过使用编译事件和IL操作允许你在编译时“处理”这些方面,例如PostSharp;而某些类库使用DynamicProxy在运行时处理;某些要求你的类继承自ContextBoundObject使用C#内建特性来supportAspects。所有的这些都有某些“不便”。你不得不使用某些外部库,做足够的性能测试来那些类库可扩展等等。而你需要的只是一个非常简单的方式来实现“隔离”,可能并不是想要完全实现AOP。记住,你的目的是隔离那些并不重要的核心代码,来让一切变得简单并且清晰!

学AOP。首先要弄清楚什么是AOP,恐怕网上无数的文章只能让我们不知所以,每篇文章要不是直接代码,就是眉毛胡子摘抄一大篇。我花了几个晚上,在网上另外找了一篇终于能让人理解什么是AOP的文章:颠覆C#王权的“魔比斯环” — 实现AOP框架的终极利器,但这篇文章没有深入讲解Emit实现,尽在最后呈现了一份Emit实现AOP的成果,让人欲罢不能。好了,啰嗦了一大篇,我想用几句话谈谈我对AOP的理解:

1 了解来历:AOP(Aspect Oriented Programming)的中文意思是“面向方面编程”。由施乐公司提出,是OOP的一种扩展。

2 弄清楚表现形式:代码侵入,他有一个新词叫织入,是AOP最重要最明显的特性。所谓织入:就是对原生方法进行拦截(这其中也包括构造方法),在执行原方法时,要在两个位置来执行我们织入的代码,如日志代码。这两个位置是before(在原代码执行之前)和after(在原代码执行之后)。例如:

原生代码:

public class A{  private int _i=0;  public int Add(int i)  {return i+=i;  }}

AOP织入代码后会变成类似如下形式:

public class A{  private int _i=0;  public int Add(int i)  {        int result=_i        Log("Add begin");result+=i;<pre name="code" class="csharp">        Log("Add end");
return result; }}

当然,还可以对源代码进行around(完全取代原来的代码)。

3 搞清楚用途:有点像黑客!目的已经很明显,但这么做的意义是什么呢?使用AOP技术,将一些与业务无关的系统功能完全独立成单独的模块,在必要时可以简捷方便的使用,比如比如安全性检查、日志记录、性能监控,异常处理等,一方面维护了OOP的“单一职责”原则,降低模块之间的耦合度,另一方面是这些处理透明化,达到简洁代码,直接提高可读性,间接提高可维护性,提高工作效率的目的。

4 知道实现方法:包括静态织入和动态织入,静态织入是在程序编译时完成的,动态织入是在运行期间完成的。

5 理解与OOP/OOD的关系:有人一句话总结:OOD/OOP面向名词领域,AOP面向动词领域。 

6 应该了解的一些基本概念: 

       。联结点(JointPoint) :一个联结程序执行过程中的一个特定点。典型的联结点有:调用一个方法;方法执行这个过程本身;类初始化;对象初始化等。联结点是 AOP 的核心概念之一,它用来定义在程序的哪里通过 AOP 加入新的逻辑。

       。切入点(Pointcut) :一个切入点是用来定义某一个通知该何时执行的一组联结点。通过定义切入点,我们可以精确地控制程序中什么组件接到什么通知。上面我们提到,一个典型的联结点是方法调用,而一个典型的切入点就是对某一个类的所在方法调用的集合。通常我们会通过组建复杂的切入点来控制通知什么时候被执行。        。通知(Advice) :在某一个特定的联结点处运行的代码称为“通知”。通知有很多种,比如在联结点之前执行的前置通知(before advice)和在联结点之后执行的后置通知(after advice) 。

        。方面(Aspect) :通知和切入点的组合叫做方面,所以,方面定义了一段程序中应该包括的逻辑,以及何时应该执行该逻辑。

        。织入(Weaving) :织入是将方面真正加入程序代码的过程。对于静态 AOP 方案而言,织入是在编译时完成的,通常是在编译过程中增加一个步骤。类似的,动态 AOP 方案则是在程序运行是动态织入的。

        。目标(Target) :如果一个对象的执行过程受到某一个 AOP 的修改,那么它就叫一个目标对象。目标对象通常也称为被通知对象。

        。引入(Introduction) :   通过引入,可以在一个对象中加入新的方法或属性,以改变它的结构,这样即使该对象的类没有实现某一个接口,也可以修改它,使之成为该接口的一个实现。   

7 C#中的AOP思想:典型的,Attribute就是一种AOP思想的简单实现,但使用很繁琐,而且反射机制总是让人感觉有很大的性能开销。其他的,通过Emit织入是似乎是完美的做法,但太复杂,一般人(至少我)弄不来。二那些需要指定继承某些类来完成AOP的做法更是一眼便被深恶痛绝(谁希望随时享受者被强奸的感觉?)。因此,我非常喜欢使用AOP 使C#代码更清晰一文提到的AOP思想,但博主仅仅实现了前置织入。本文随后我将对其进一步完善,完成后置织入的处理。完成后的代码基本上已经可以投入实用了。

二、代码实现

1 核心代码部分:AspectF.cs

using System;using System.Collections.Generic;using System.Linq;using System.Web;using System.Diagnostics;namespace aoptest {    /// <summary>    /// AspectF,AOP基础类    /// </summary>    public class AspectF    {        /// <summary>        /// 获取AspectF实例        /// </summary>        public static AspectF Define        {            [DebuggerStepThrough]            get            {                return new AspectF();            }        }         /// <summary>        /// Action执行的出错信息        /// </summary>        /// <typeparam name="T"></typeparam>        [DebuggerStepThrough]        public class ActionError<T> : Chiefan.Common.ActionResult<T> { }        /// <summary>        /// Action重试过程中的出错处理委托        /// </summary>        /// <param name="e"></param>        /// <param name="cancel"></param>        public delegate void RetryErrorHandle(Exception e, ref bool cancel);       /// <summary>        /// DO的执行结果状态        /// </summary>        public ActionError<Exception> Error { get { return error; } }                /// <summary>        /// 是否启用计算并打印执行耗时        /// </summary>        public bool AllowHowLong { get; set; }        /// <summary>        /// 执行一个没有返回值的workItem        /// </summary>        /// <param name="work">action</param>        [DebuggerStepThrough]        public void Do(Action work)        {            System.Diagnostics.StackTrace stackTrace = new System.Diagnostics.StackTrace();            System.Diagnostics.StackFrame sf = stackTrace.GetFrame(1);//[0]为本身的方法 [1]为调用方法            string abc= string.Format("{0}.{1}", sf.GetMethod().ReflectedType.FullName, sf.GetMethod().Name);//类名            try            {                if (this.Chain == null)                {                    this.WriteLog(Logger.LogType.Debug, "方法[{0}]执行时间计时开始", abc);                    int beginTime = Environment.TickCount;                    work();                    this.WriteLog(Logger.LogType.Debug, "方法[{0}]执行时间共计{1}毫秒", abc, Environment.TickCount - beginTime);                }                else                {                    this.Chain(() => {                        this.WriteLog(Logger.LogType.Debug, "方法[{0}]执行时间计时开始", abc);                        int beginTime = Environment.TickCount;                        work();                        this.WriteLog(Logger.LogType.Debug, "方法[{0}]执行时间共计{1}毫秒", abc, Environment.TickCount - beginTime);                    });                }            }            catch (Exception ex)            {                error.ErrorCode = -1;                error.ErrorMessage = "未知错误";                error.Data = ex;                this.WriteLog(Logger.LogType.Error, "{0} - {1}", abc, ex.Message);            }            if (this.PostChain != null)                this.PostChain(() => { });        }        /// <summary>        /// 绑定一个新的委托        /// </summary>        /// <param name="newAspectDelegate">委托</param>        /// <returns>返回AspectF实例</returns>        [DebuggerStepThrough]        public AspectF Combine(Action<Action> newAspectDelegate, bool isPost=false)        {            if (isPost)            {                if (this.PostChain == null)                {                    this.PostChain = newAspectDelegate;                }                else                {                    Action<Action> existingChain = this.PostChain;                    Action<Action> callAnother = (work) =>                        existingChain(() => newAspectDelegate(work));                    this.PostChain = callAnother;                }            }            else            {                if (this.Chain == null)                {                    this.Chain = newAspectDelegate;                }                else                {                    Action<Action> existingChain = this.Chain;                    Action<Action> callAnother = (work) =>                        existingChain(() => newAspectDelegate(work));                    this.Chain = callAnother;                }            }            return this;        }        /// <summary>        /// 打印日志        /// </summary>        /// <param name="logType"></param>        /// <param name="format"></param>        /// <param name="args"></param>        [DebuggerStepThrough]        public void WriteLog(Logger.LogType logType, string format, params object[] args)        {            switch (logType)            {                case Logger.LogType.Error:                    log.Error(format, args);                    break;                case Logger.LogType.Debug:                    log.Debug(format, args);                    break;                case Logger.LogType.Warn:                    log.Warn(format, args);                    break;                case Logger.LogType.Fatal:                    log.Fatal(format, args);                    break;                case Logger.LogType.Info:                default:                    log.Info(format, args);                    break;            }        }        Action<Action> Chain = null;        Action<Action> PostChain = null;        Logger.ILog log = Logger.Log.Instance;        ActionError<Exception> error = new ActionError<Exception>();    }}

2 扩展代码部分

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Diagnostics;namespace aoptest{    using Logger = Chiefan.eSail2.Logger;    /// <summary>    /// AspectF扩展类    /// </summary>    public static class AspectExt    {        /// <summary>        /// 记录日志        /// </summary>        /// <param name="aspect"></param>        /// <param name="logType">日志类型:error|debug|info</param>        /// <param name="message"></param>        /// <param name="isPost">是否后置执行</param>        /// <returns></returns>        [DebuggerStepThrough]        public static AspectF Log(this AspectF aspect, Logger.LogType logType, string message, bool isPost = false)        {            return aspect.Combine((work) =>            {                aspect.WriteLog(logType, "{0}", message);                work();            }, isPost);        }        /// <summary>        /// 性能监视        /// </summary>        /// <param name="aspects"></param>        /// <returns></returns>        [DebuggerStepThrough]        public static AspectF HowLong(this AspectF aspects)        {            aspects.AllowHowLong = true;            return aspects;        }        /// <summary>        /// Do的失败后重复执行版本        /// </summary>        /// <param name="aspects"></param>        /// <param name="errorHandle">错误处理句柄</param>        /// <param name="failedHandle">失败处理句柄</param>        /// <param name="retryDuration">重试间隔(毫秒)</param>        /// <param name="retryCount">重试次数</param>        /// <returns></returns>        [DebuggerStepThrough]        public static AspectF Retry(this AspectF aspects,            AspectF.RetryErrorHandle errorHandle, Action failedHandle,             int retryCount = 1, int retryDuration = 1000)        {            return aspects.Combine((work) =>                Retry(retryDuration, retryCount, errorHandle, failedHandle, work));        }        /// <summary>        /// Retry        /// </summary>        /// <param name="retryDuration">retry的时间间隔</param>        /// <param name="retryCount">retry次数</param>        /// <param name="errorHandler">错误处理句柄</param>        /// <param name="retryFailed">retry失败处理句柄</param>        /// <param name="work">真实的委托内容</param>        [DebuggerStepThrough]        public static void Retry(int retryDuration, int retryCount,            AspectF.RetryErrorHandle errorHandler, Action retryFailed, Action work)        {            bool cancel = false;            bool success = false;            do            {                try                {                    success = true;                    work();                }                catch (Exception ex)                {                    errorHandler(ex, ref cancel);                    System.Threading.Thread.Sleep(retryDuration);                }            } while (retryCount-- > 0);            if (!success)                retryFailed();        }        [DebuggerStepThrough]        public static AspectF Delay(this AspectF aspect, int milliseconds)        {            return aspect.Combine((work) =>            {                System.Threading.Thread.Sleep(milliseconds);                work();            });        }        [DebuggerStepThrough]        public static AspectF MustBeNonNull(this AspectF aspect, Action<AspectF> errorHandle, params KeyValuePair<string,object>[] args)        {            return aspect.Combine((work) =>            {                for (int i = 0; i < args.Length; i++)                {                    object arg = args[i].Value;                    if (arg == null)                    {                        string errorMessage = string.Format("{0}, Action被终止.", args[i].Key);                        aspect.Error.ErrorCode = -1;                        aspect.Error.ErrorMessage = errorMessage;                        aspect.Error.Data = new ArgumentException(errorMessage);                        System.Diagnostics.StackTrace stackTrace = new System.Diagnostics.StackTrace();                        System.Diagnostics.StackFrame sf = stackTrace.GetFrame(1);//[0]为本身的方法 [1]为调用方法                        string abc = string.Format("{0}.{1}", sf.GetMethod().ReflectedType.FullName, sf.GetMethod().Name);//类名                        aspect.WriteLog(Logger.LogType.Error, "{0} - {1}", abc, errorMessage);                        errorHandle(aspect);                    }                }                work();            });        }        public static AspectF MustBeNonDefault<T>(this AspectF aspect, Action<AspectF> errorHandle, params KeyValuePair<string,T>[] args) where T : IComparable        {            return aspect.Combine((work) =>            {                T defaultvalue = default(T);                for (int i = 0; i < args.Length; i++)                {                    T arg = args[i].Value;                    if (arg == null || arg.Equals(defaultvalue))                    {                        //throw new ArgumentException(string.Format("Parameter at index {0} is null", i));                        string errorMessage = string.Format("{0}, Action被终止.", args[i].Key);                        aspect.Error.ErrorCode = -1;                        aspect.Error.ErrorMessage = errorMessage;                        aspect.Error.Data = new ArgumentException(errorMessage);                        System.Diagnostics.StackTrace stackTrace = new System.Diagnostics.StackTrace();                        System.Diagnostics.StackFrame sf = stackTrace.GetFrame(1);//[0]为本身的方法 [1]为调用方法                        string abc = string.Format("{0}.{1}", sf.GetMethod().ReflectedType.FullName, sf.GetMethod().Name);//类名                        aspect.WriteLog(Logger.LogType.Error, "{0} - {1}", abc, errorMessage);                        errorHandle(aspect);                    }                }                work();            });        }        public static AspectF WhenTrue(this AspectF aspect, params Func<bool>[] conditions)        {            return aspect.Combine((work) =>            {                int i = 0;                foreach (Func<bool> condition in conditions)                {                    if (!condition())                    {                        string errorMessage = string.Format("执行条件[{0}]不满足要求,Action被终止.", i);                        aspect.Error.ErrorCode = -1;                        aspect.Error.ErrorMessage = errorMessage;                        aspect.Error.Data = new ArgumentException(errorMessage);                        System.Diagnostics.StackTrace stackTrace = new System.Diagnostics.StackTrace();                        System.Diagnostics.StackFrame sf = stackTrace.GetFrame(1);//[0]为本身的方法 [1]为调用方法                        string abc = string.Format("{0}.{1}", sf.GetMethod().ReflectedType.FullName, sf.GetMethod().Name);//类名                        aspect.WriteLog(Logger.LogType.Error, "{0} - {1}", abc, errorMessage);                        return;                    }                    i++;                }                work();            });        }    }}
3 使用范例:

        public static void InsertCustomerTheEasyWay(string firstName, string lastName, int age,            Dictionary<string, string> attributes)        {            AspectF.Define                .Log(Logger.LogType.Debug, "Inserting customer the easy way1")                .Log(Logger.LogType.Debug, "Inserting customer the easy way2")                .Log(Logger.LogType.Debug, "Inserting customer the ooo2 way", true)                .Log(Logger.LogType.Debug, "Inserting customer the ooo1 way", true)                .HowLong()                .Do(() => { Console.WriteLine("ok!"); })                //.Do(() => {throw new Exception("a dog is at ahead!"); })                //.Do<string>(new Func<string>(() => { Console.WriteLine("ok"); return "okey!"; }));                ;        }
准备在项目上试用一下,试用成功后我将继续更新本文。

欢迎共同探讨,转载请保留本文连接:C#之AOP初探

0 0
原创粉丝点击