Attribute的妙用 ---- 攔截器(過濾器)


一、何為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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM