以下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類型,可以隨意設置,值越小優先級越高,只要讓引擎能從數字層面區分優先順序即可。
另外,切面的優先順序不一樣,引擎最終編譯出來的代碼也是不一樣的,具體可以反編譯查看。不管誰先執行誰后執行,總的來說結果沒什么變化的。我個人更喜歡將異常處理切面優先級提高些,這樣更加符合平時手寫代碼的風格。