Postsharp基本用法——方法、属性拦截与异常处理

以下Demo代码基于 .NET Core 演示了Postsharp的基本使用方法,稍作修改(反射部分有些许差异)也适用于.NET Framework。

更多高级使用方法详见官方文档。http://samples.postsharp.net/

 

代码(注意,这段代码编译后会有警告,解决方案见文末):

  1 using System;
  2 using System.Linq;
  3 using PostSharp.Aspects;
  4 using PostSharp.Serialization;
  5 
  6 namespace NetCoreConsole
  7 {
  8   class Program
  9   {
 10     static void Main(string[] args)
 11     {
 12       var result = Calc(5, 6);
 13       Console.WriteLine($"计算结果:{result}");
 14       Console.WriteLine(">>>>>>>>>>>>>>方法拦截测试完毕\r\n");
 15 
 16 
 17       PropertyTest = -1;
 18       Console.WriteLine(">>>>>>>>>>>>>>属性拦截测试(setter)完毕\r\n");
 19 
 20 
 21       var x = PropertyTest;
 22       Console.WriteLine(">>>>>>>>>>>>>>属性拦截测试(getter)完毕\r\n");
 23 
 24       Console.ReadKey();
 25     }
 26 
 27 
 28     /// <summary>
 29     /// 方法拦截测试 + 异常处理
 30     /// </summary>
 31     /// <param name="x"></param>
 32     /// <param name="y"></param>
 33     /// <returns></returns>
 34     [HowToUse, ExceptionHandle]
 35     private static int Calc(int x, int y)
 36     {
 37       int a = 1;
 38       int b = 0;
 39       int c = a / b;
 40 
 41       return x + y;
 42     }
 43 
 44     private static int _propertyTest;
 45 
 46     /// <summary>
 47     /// 属性拦截测试
 48     /// 注:可以标记在整个属性上,也可以分别单独标记在 【getter】 或者 【setter】 上
 49     /// </summary>
 50     [HowToUse, ExceptionHandle]
 51     private static int PropertyTest
 52     {
 53 
 54       get
 55       {
 56         return _propertyTest;
 57       }
 58 
 59       // [HowToUse]
 60       set
 61       {
 62         if (value <= 0)
 63         {
 64           throw new ArgumentException($"属性值必须大于0");
 65         }
 66 
 67         _propertyTest = value;
 68       }
 69     }
 70   }
 71 }
 72 
 73 /// <summary>
 74 /// 方法拦截测试
 75 /// </summary>
 76 [PSerializable]
 77 [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property)]
 78 public class HowToUseAttribute : MethodInterceptionAspect
 79 {
 80   /// <summary>
 81   /// 方法执行拦截
 82   /// </summary>
 83   /// <param name="args"></param>
 84   public override void OnInvoke(MethodInterceptionArgs args)
 85   {
 86     var methodBase = args.Method;
 87 
 88     // 如果是 .NET Framework 这里的System.Reflection.MethodInfo 应该替换为 System.Reflection.RuntimeMethodInfo
 89     var returnType = ((System.Reflection.MethodInfo)methodBase).ReturnType.FullName;
 90      
 91     // 方法形式参数列表字符
 92     var paramListString = methodBase.GetParameters().Aggregate(string.Empty,
 93       (current, parameter) => current + $"{parameter.ParameterType.FullName} {parameter.Name}, ").Trim(',', ' ');
 94 
 95     // 方法签名
 96     // var signatures =  $"{returnType} {methodBase.Name}({paramListString})";
 97     var signatures = methodBase.ToString();
 98 
 99     Console.WriteLine($"被拦截的方法签名:{signatures}");
100 
101     // 方法实际参数列表字符
102     var argsString = args.Arguments
103       .Aggregate(string.Empty, (current, p) => current + $"{p.GetType().FullName} ---> 值:{p}, ").Trim(',', ' ');
104 
105     Console.WriteLine($"被拦截的方法输入参数:{argsString}");
106 
107     // 处理(执行被拦截的方法)
108     args.Proceed();
109 
110     // 异步执行
111     // args.ProceedAsync();
112 
113     var returnValue = args.ReturnValue;
114 
115     Console.WriteLine($"方法返回值:{returnValue}");
116   }
117 }
118 
119 /// <summary>
120 /// 异常处理
121 /// </summary>
122 [PSerializable]
123 [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property)]
124 public class ExceptionHandleAttribute : OnExceptionAspect
125 {
126   public override void OnException(MethodExecutionArgs args)
127   {
128     // 设置流程行为(继续执行,不抛出)
129     args.FlowBehavior = FlowBehavior.Continue;
130 
131     Console.WriteLine($"发生异常:{args.Exception.GetType().FullName} ----> {args.Exception.Message}");
132   }
133 }

 

这段代码可以正常编译,但会有警告。警告内容类似下图:

 

            (图1)

 

大意就是说在我们的 Calc方法上存在冲突的切面。

为什么会产生这样的警告呢,因为我们使用了两个类型的Aspect,一个是异常处理,一个是方法拦截(属性也可以认为是Getter和Setter两个方法的结合)。 

异常处理切面(Aspect)期望包装我们的目标方法,方法拦截切面(Aspect)也是如此,但是这两个切面并不是强排序的,它们的执行顺序并不确定,这就是冲突。解决方法很简单,请对比图2与图3中代码的区别:

              (图2)

 

               (图3)

 

 

没错,解决方法就是使用 [  AspectPriority  ]属性手动指定切面的优先顺序。属性值是Int类型,可以随意设置,值越小优先级越高,只要让引擎能从数字层面区分优先顺序即可。

另外,切面的优先顺序不一样,引擎最终编译出来的代码也是不一样的,具体可以反编译查看。不管谁先执行谁后执行,总的来说结果没什么变化的。我个人更喜欢将异常处理切面优先级提高些,这样更加符合平时手写代码的风格。

 

posted @ 2018-11-26 15:51  X-Cracker  阅读(2169)  评论(0编辑  收藏  举报