首页 > 代码库 > 使用AOP 使C#代码更清晰 转yanghua_kobe

使用AOP 使C#代码更清晰 转yanghua_kobe

http://blog.csdn.net/yanghua_kobe/article/details/6917228

 

简介如果你很熟悉面向方面编程(AOP),你就会知道给代码增加“切面”可以使代码更清晰并且具有可维护性。但是AOP通常都依赖于第三方类库或者硬编码的.net特性来工作。虽然这些实现方式的好处大于它们的复杂程度,但是我仍然在寻找一种实现AOP的更为简单的方式,来试我的代码更为清晰。我将它们单独移出来,并命名为AspectF。Aspect Oriented Programming (AOP)的背景“切面”指的是那些在你写的代码中在项目的不同部分且有相同共性的东西。它可能是你代码中处理异常、记录方法调用、时间处理、重新执行一些方法等等的一些特殊方式。如果你没有使用任何面向切面编程的类库来做这些事情,那么在你的整个项目中将会遗留一些很简单而又重复的代码,它将使你的代码很难维护。例如,在你的业务逻辑层有些方法需要被记录,有些异常需要被处理,有些执行需要计时,数据库操作需要重试等等。所以,也许你会写出下面这样的代码。[csharp] view plaincopyprint?    public bool InsertCustomer(string firstName, string lastName, int age,           Dictionary<string, string> attributes)      {          if (string.IsNullOrEmpty(firstName))               throw new ApplicationException("first name cannot be empty");          if (string.IsNullOrEmpty(lastName))              throw new ApplicationException("last name cannot be empty");          if (age < 0)              throw new ApplicationException("Age must be non-zero");          if (null == attributes)              throw new ApplicationException("Attributes must not be null");                    // Log customer inserts and time the execution          Logger.Writer.WriteLine("Inserting customer data...");          DateTime start = DateTime.Now;                    try          {              CustomerData data = http://www.mamicode.com/new CustomerData();  "Successfully inserted customer data in "                       + (DateTime.Now-start).TotalSeconds + " seconds");              }              return result;          }          catch (Exception x)          {              // Try once more, may be it was a network blip or some temporary downtime              try              {                  CustomerData data = http://www.mamicode.com/new CustomerData();  "Successfully inserted customer data in "                           + (DateTime.Now-start).TotalSeconds + " seconds");                  }                  return result;              }              catch               {                  // Failed on retry, safe to assume permanent failure.                  // Log the exceptions produced                  Exception current = x;                  int indent = 0;                  while (current != null)                  {                      string message = new string(Enumerable.Repeat(‘\t‘, indent).ToArray())                          + current.Message;                      Debug.WriteLine(message);                      Logger.Writer.WriteLine(message);                      current = current.InnerException;                      indent++;                  }                  Debug.WriteLine(x.StackTrace);                  Logger.Writer.WriteLine(x.StackTrace);                  return false;              }          }      }   你会看到上面只有两行关键代码,它调用了CustomerData实例的一个方法插入了一个Customer。但去实现这样的业务逻辑,你真的很难去照顾所有的细节(日志记录、重试、异常处理、操作计时)。项目越成熟,在你的代码中需要维护的这些“边边角角”就更多了。所以你肯定经常会到处拷贝这些“样板”代码,但只在这些样板内写少了真是的东西。这多不值!你不得不对每个业务逻辑层的方法都这么做。比如现在你想在你的业务逻辑层中增加一个UpdateCustomer方法。你不得不再次拷贝所有的这些“样板”,然后将两行关键代码加入其中。思考这样的场景,你需要做出一个项目级别的改变——针对如何处理异常。你不得不处理你写的这“上百”的方法,然后一个一个地修改它们。如果你想修改计时的逻辑,做法同样如此。面向切面编程就可以很好地处理这些问题。当你采用AOP,你会以一种很酷的方式来实现它:[csharp] view plaincopyprint?    [EnsureNonNullParameters]      [Log]      [TimeExecution]      [RetryOnceOnFailure]      public void InsertCustomerTheCoolway(string firstName, string lastName, int age,          Dictionary<string, string> attributes)      {          CustomerData data = http://www.mamicode.com/new CustomerData();  "Inserting customer the easy way")              .HowLong(Logger.Writer, "Starting customer insert",               "Inserted customer in {1} seconds")              .Retry()              .Do(() =>                  {                      CustomerData data = http://www.mamicode.com/new CustomerData();  "newAspectDelegate">A delegate that offers an aspect‘s behavior.           /// It‘s added into the aspect chain</param>          /// <returns></returns>          [DebuggerStepThrough]          public AspectF Combine(Action<Action> newAspectDelegate)          {              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;          }  这里Combine方法操作的是被“切面”扩展方法传递过来的委托,例如Log,然后它将该委托压入之前加入的一个“切面”的委托中,来保证第一个切面调用第二个,第二个调用第三个,知道最后一个调用真实的(你想要真正执行的)代码。Do/Return方法做最后的执行操作。[csharp] view plaincopyprint?    /// <summary>      /// Execute your real code applying the aspects over it      /// </summary>      /// <param name="work">The actual code that needs to be run</param>      [DebuggerStepThrough]      public void Do(Action work)      {          if (this.Chain == null)          {              work();          }          else          {              this.Chain(work);          }      }  就是这些,现在你有一个非常简单的方式来分隔那些你不想过度关注的代码,并使用C#享受AOP风格的编程模式。AspectF类还有其他几个方便的“切面”,大致如下(当然你完全可以DIY你自己的‘切面’)。[csharp] view plaincopyprint?    public static class AspectExtensions          {              [DebuggerStepThrough]              public static void DoNothing()              {                    }                    [DebuggerStepThrough]              public static void DoNothing(params object[] whatever)              {                    }                    [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, params object[] args)              {                  return aspect.Combine((work) =>                  {                      for (int i = 0; i < args.Length; i++)                      {                          object arg = args[i];                          if (arg == null)                          {                              throw new ArgumentException(string.Format("Parameter at index {0} is null", i));                          }                      }                      work();                  });              }                    [DebuggerStepThrough]              public static AspectF MustBeNonDefault<T>(this AspectF aspect, params T[] args) where T : IComparable              {                  return aspect.Combine((work) =>                  {                      T defaultvalue = http://www.mamicode.com/default(T);  "Parameter at index {0} is null", i));                          }                      }                      work();                  });              }                    [DebuggerStepThrough]              public static AspectF WhenTrue(this AspectF aspect, params Func<bool>[] conditions)              {                  return aspect.Combine((work) =>                  {                      foreach (Func<bool> condition in conditions)                      {                          if (!condition())                          {                              return;                          }                      }                      work();                  });              }                    [DebuggerStepThrough]              public static AspectF RunAsync(this AspectF aspect, Action completeCallback)              {                  return aspect.Combine((work) => work.BeginInvoke(asyncresult =>                  {                      work.EndInvoke(asyncresult); completeCallback();                  }, null));              }                    [DebuggerStepThrough]              public static AspectF RunAsync(this AspectF aspect)              {                  return aspect.Combine((work) => work.BeginInvoke(asyncresult =>                  {                      work.EndInvoke(asyncresult);                  }, null));              }          }  现在,你已经拥有了一个简洁的方式来隔离那些细枝末节的代码,去享受AOP形式的编程而无需使用任何“笨重”的框架。

 

使用AOP 使C#代码更清晰 转yanghua_kobe