一、何為Attribute
下面是微軟官方對Attribute的解釋:
公共語言運行時允許你添加類似關鍵字的描述聲明,叫做Attributes,它對程序中的元素進行標注,如類型、字段、方法和屬性等。Attributes和Microsoft .NET Framework文件的元數據保存在一起,可以用來向運行時描述你的代碼,或者在程序運行的時候影響應用程序的行為。
通俗地理解,就是對目標對象(程序集、類、方法等)進行擴展,使得在運行時可以獲取到被擴展對象的額外的信息,通過額外的信息來影響目標對象的行為。上面這句話純粹是個人的理解,如有不妥希望指教。
二、使用Attribute
現在我有一個需求,創建一個包含 三個靜態方法的類,如果某個方法被打上了標簽,並且標簽的Flag是1,那么就執行該方法,否則就不執行。看起來有點像過濾器,那么如何來實現這個小需求呢?首先要創建一個靜態類MethodToRun,該類有三個靜態方法分別是Run、Walk、Go,代碼如下:
1 public class MethodToRun 2 { 3 public static void Run () 4 { 5 Console.WriteLine("Run Run Hurry Up!"); 6 Console.ReadLine(); 7 } 8 9 public static void Walk() 10 { 11 Console.WriteLine("Walk Slowly~"); 12 Console.ReadLine(); 13 } 14 15 public static void Go() 16 { 17 Console.WriteLine("Go Go Go!"); 18 Console.ReadLine(); 19 } 20 }
好了,有了以上的類,接下來開始創建我們自定義的Attribute,為了和Property屬性做個區分,我稱之為特性。取個名字叫ExcuteAttribute,擁有一個Flag屬性,代碼如下:
1 [AttributeUsage(AttributeTargets.Method)] 2 public class ExcuteAttribute : Attribute 3 { 4 public int Flag { get; set; } 5 }
上述代碼第一行指定了該特性作用的范圍,回頭看下我們之前說的一句話:
就是對目標對象(程序集、類、方法等)進行擴展,使得在運行時可以獲取到被擴展對象的額外的信息,通過額外的信息來影響目標對象的行為。
這里的AttributeUsage中的參數AttributeTargets就是目標對象,它是一個枚舉類型,具體的枚舉如下:
1 //指定可以對它們應用屬性的應用程序元素。 2 [ComVisible(true)] 3 [Flags] 4 public enum AttributeTargets 5 { 6 //可以對程序集應用屬性。 7 Assembly = 1, 8 9 //可以對模塊應用屬性。 10 Module = 2, 11 12 //可以對類應用屬性。 13 Class = 4, 14 15 //可以對結構應用屬性,即值類型。 16 Struct = 8, 17 18 //可以對枚舉應用屬性。 19 Enum = 16, 20 21 //可以對構造函數應用屬性。 22 Constructor = 32, 23 24 //可以對方法應用屬性。 25 Method = 64, 26 27 //可以對屬性 (Property) 應用屬性 (Attribute)。 28 Property = 128, 29 30 //可以對字段應用屬性。 31 Field = 256, 32 33 //可以對事件應用屬性。 34 Event = 512, 35 36 //可以對接口應用屬性。 37 Interface = 1024, 38 39 //可以對參數應用屬性。 40 Parameter = 2048, 41 42 //可以對委托應用屬性。 43 Delegate = 4096, 44 45 //可以對返回值應用屬性。 46 ReturnValue = 8192, 47 48 //可以對泛型參數應用屬性。 49 GenericParameter = 16384, 50 51 //可以對任何應用程序元素應用屬性。 52 All = 32767 53 }
標簽創建完成了,我們修改一下MethodToRun這個類,加上標簽,代碼如下:
public class MethodToRun { [Excute(Flag =1)] public static void Run () { Console.WriteLine("Run Run Hurry Up!"); Console.ReadLine(); } public static void Walk() { Console.WriteLine("Walk Slowly~"); Console.ReadLine(); } [Excute(Flag =2)] public static void Go() { Console.WriteLine("Go Go Go!"); Console.ReadLine(); } }
其中,Run方法和Go方法加上了標簽,且Flag的值分別為1和2,我們的需求是貼了標簽並且Flag為1的方法被執行,下面來看下關鍵的調用代碼:
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 //獲取MethodToRun類的靜態方法集合 6 MethodInfo[] methods = typeof(MethodToRun).GetMethods(BindingFlags.Public|BindingFlags.Static); 7 foreach (var method in methods) 8 { 9 MethodInfo info = method; 10 //獲取每個方法上的Attributes集合 11 var attributes = info.GetCustomAttributes(typeof(Attribute), false); 12 13 foreach (var attri in attributes) 14 { 15 //如果自定義的標簽是指定的標簽則符合條件 16 if(attri is ExcuteAttribute) 17 { 18 ExcuteAttribute exe = attri as ExcuteAttribute; 19 //執行Flag為1的方法 20 if(exe.Flag == 1) 21 { 22 info.Invoke(null, null); 23 } 24 } 25 } 26 } 27 Console.ReadLine(); 28 } 29 }
運行結果:
整個過程就是通過反射獲取目標集合,本例子中的MethodToRun類中的靜態方法(因為標簽都貼在了靜態方法中,且AttributeTargets指定的目標也是靜態方法),然后獲取方法上的自定義標簽集合,相當於方法的額外的信息,通過這些額外的信息來影響方法的執行,現在再回頭看看第一小節的粗體部分:
就是對目標對象(程序集、類、方法等)進行擴展,使得在運行時可以獲取到被擴展對象的額外的信息,通過額外的信息來影響目標對象的行為。
以上是個人比較淺薄的理解,如果您有更深層次的理解,歡迎討論,互相學習。
三、自己寫攔截器
根據上面的表述,結合ASP.NET MVC常用的攔截器功能,自己實現一個極簡的攔截器。首先定義一個接口ICustomFilter,包含兩個接口方法,OnBeforeAction和OnAfterAction,代碼如下:
1 /// <summary> 2 /// 自定義攔截器 3 /// </summary> 4 public interface ICustomFilter 5 { 6 //Action執行之前執行 7 void OnBeforeAction(); 8 9 //Action執行之后執行 10 void OnAfterAction(); 11 }
再定義一個Attribute實現該接口:
1 public class CustomFilterAttribute : Attribute, ICustomFilter 2 { 3 public void OnAfterAction() 4 { 5 Console.WriteLine("Action 執行之后進行攔截!"); 6 } 7 8 public void OnBeforeAction() 9 { 10 Console.WriteLine("Action 執行之前進行攔截!"); 11 } 12 }
准備工作完成了,接下來給目標方法打上標簽,修改下MethodToRun中的代碼:
public class MethodToRun { [Excute(Flag =1)] public static void Run () { Console.WriteLine("Run Run Hurry Up!"); Console.ReadLine(); } [CustomFilter] public static void Walk() { Console.WriteLine("Walk Slowly~"); Console.ReadLine(); } [Excute(Flag =2)] public static void Go() { Console.WriteLine("Go Go Go!"); Console.ReadLine(); } }
其中Walk方法添加了[CustomAttribute]特性,接下來看下調用代碼是如何實現在Walk執行前后分別執行OnBeforeAction和OnAfterAction方法的,代碼如下:
class Program { static void Main(string[] args) { MethodInfo[] methods = typeof(MethodToRun).GetMethods(BindingFlags.Public|BindingFlags.Static); foreach (var method in methods) { MethodInfo info = method; var attributes = info.GetCustomAttributes(typeof(Attribute), false); foreach (var attri in attributes) { if(attri is ExcuteAttribute) { ExcuteAttribute exe = attri as ExcuteAttribute; if(exe.Flag == 1) { //info.Invoke(null, null); } } //這里是重點 if(attri is CustomFilterAttribute) { CustomFilterAttribute cust = attri as CustomFilterAttribute; cust.OnBeforeAction(); info.Invoke(null, null); cust.OnAfterAction(); } } } Console.ReadLine(); } }
現在跑一下看看效果
上述小例子只是一個小小的應用,在.Net體系中,Attribute隨處可見,其應用范圍十分廣泛。
出處:http://www.cnblogs.com/xhb-bky-blog/p/7840265.html