以下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(">>>>>>>>>>>>>>方法拦截测试完毕 "); 15 16 17 PropertyTest = -1; 18 Console.WriteLine(">>>>>>>>>>>>>>属性拦截测试(setter)完毕 "); 19 20 21 var x = PropertyTest; 22 Console.WriteLine(">>>>>>>>>>>>>>属性拦截测试(getter)完毕 "); 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类型,可以随意设置,值越小优先级越高,只要让引擎能从数字层面区分优先顺序即可。
另外,切面的优先顺序不一样,引擎最终编译出来的代码也是不一样的,具体可以反编译查看。不管谁先执行谁后执行,总的来说结果没什么变化的。我个人更喜欢将异常处理切面优先级提高些,这样更加符合平时手写代码的风格。