【博主】反骨仔 【原文地址】http://www.cnblogs.com/liqingwen/p/5911289.htmlhtml
特性具備如下屬性:數組
(1)特性可向程序中添加元數據。元數據是有關在程序中定義的類型的信息。全部的 .NET 程序集都包含指定的一組元數據,這些元數據描述在程序集中定義的類型和類型成員。能夠添加自定義特性,以指定所需的任何附加信息。安全
(2)能夠將一個或多個特性應用到整個程序集、模塊或較小的程序元素(如類和屬性)。app
(3)特性能夠與方法和屬性相同的方式接受參數。ide
(4)程序能夠使用反射檢查本身的元數據或其餘程序內的元數據。函數
特性能夠放置在幾乎全部的聲明中(但特定的特性可能限制在其上有效的聲明類型)。在 C# 中,特性的指定方法爲:將括在方括號中的特性名置於其應用到的實體的聲明上方。它必須位於所應用於的元素的緊前面並與該元素在同一行。優化
1 [Serializable] //使用特性 SerializableAttribute 2 internal class MyClass 3 { 4 [DllImport("user32.dll")] //使用特性 DllImportAttribute 5 private static extern void Do(); 6 7 #region 一個聲明上可放置多個特性 8 9 private void MethodA([In][Out]ref double n) { } 10 private void MethodB([In, Out]ref double n) { } 11 12 #endregion 一個聲明上可放置多個特性 13 14 #region 某些特性對於給定實體能夠指定屢次 15 16 [Conditional("DEBUG"), Conditional("TEST1")] 17 private void TraceMethod() { } 18 19 #endregion 某些特性對於給定實體能夠指定屢次 20 }
【注意】根據約定,全部特性名稱都以單詞「Attribute」結束,以便將它們與「.NET Framework」中的其餘項區分。可是,在代碼中使用特性時,不須要指定 attribute 後綴。ui
許多特性都有參數,而這些參數能夠是定位參數、未命名參數或命名參數。任何定位參數都必須按特定順序指定而且不能省略,而命名參數是可選的且能夠按任意順序指定。首先指定定位參數。例如,這三個特性是等效的:this
1 [DllImport("user32.dll")] 2 [DllImport("user32.dll", SetLastError=false, ExactSpelling=false)]
第一個參數(DLL 名稱)是定位參數而且老是第一個出現,其餘參數爲命名參數。在這種狀況下,兩個命名參數均默認爲 false,所以可將其省略。spa
特性的目標是應用該特性的實體。例如,特性能夠應用於類、特定方法或整個程序集。默認狀況下,特性應用於它後面的元素。可是,您也能夠顯式標識要將特性應用於方法仍是它的參數或返回值。
若要顯式標識特性目標,語法:
[target : attribute-list]
C# | 適用對象 |
assembly | 整個程序集 |
module | 當前程序集模塊 |
field | 在類或結構中的字段 |
event | event |
method | 方法或 get 和 set 屬性訪問器 |
param | 方法參數或 set 屬性訪問器參數 |
property | 屬性 |
return | 方法、屬性索引器或 get 屬性訪問器的返回值 |
type | 結構、類、接口、枚舉或委託 |
//示例:將特性應用於程序集和模塊 [assembly: AssemblyTitle("assembly 4.6.1")] [module: CLSCompliant(true)]
1 //示例:將特性應用於方法、方法參數和方法返回值 2 3 //默認:應用於方法 4 [SomeAttr] 5 int Method1() { return 0; } 6 7 //指定應用於方法 8 [method: SomeAttr] 9 int Method2() { return 0; } 10 11 //指定應用於返回值 12 [return: SomeAttr] 13 int Method3() { return 0; }
如下列表包含特性的幾個常見用途:
(1)在 Web 服務中,使用 WebMethod 特性來標記方法,以指示該方法應該可經過 SOAP 協議進行調用。
(2)描述當與本機代碼進行交互操做時如何封送方法參數。有關更多信息。
(3)描述類、方法和接口的 COM 屬性。
(4)使用 DllImportAttribute 類調用非託管代碼。
(5)在標題、版本、說明或商標方面描述您的程序集。
(6)描述要持久性序列化類的哪些成員。
(7)描述如何映射類成員和 XML 節點以便進行 XML 序列化。
(8)描述方法的安全要求。
(9)指定用於強制安全性的特性。
(10)由實時 (JIT) 編譯器控制優化,以便易於調試代碼。
(11)獲取有關調用方的信息的方法。
經過定義一個特性類,能夠建立您本身的自定義特性。該特性類直接或間接地從 Attribute 派生,有助於方便快捷地在元數據中標識特性定義。
1 /// <summary> 2 /// 角色特性 3 /// </summary> 4 /// RoleAttribute:特性的名稱,繼承 Attribute,爲自定義特性 5 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)] 6 public class RoleAttribute : Attribute 7 { 8 private string _name; 9 10 /// <summary> 11 /// 啓用標識 12 /// </summary> 13 /// IsEnable:命名參數 14 public bool IsEnable { get; set; } 15 16 /// <summary> 17 /// 構造函數 18 /// </summary> 19 /// <param name="name"></param> 20 /// name:定位參數 21 public RoleAttribute(string name) 22 { 23 _name = name; 24 } 25 }
1 [Role("Me", IsEnable = true)] //調用特性的方式 2 public class OurClass 3 { 4 5 }
構造函數的參數是自定義特性的定位參數,任何公共的讀寫字段或屬性都是命名參數。【注意】 AttributeUsage 特性,在這裏它使得 Role 特性僅在類和 struct 聲明中有效。
1 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = true)] //AllowMultiple:該值指示可否爲一個程序屢次使用該特性 2 public class RoleAttribute : Attribute 3 { 4 //... ... 5 }
1 [Role("You")] //在同一個類上屢次使用 2 [Role("Me", IsEnable = true)] 3 public class OurClass 4 { 5 //... ... 6 }
【注意】若是特性類包含一個屬性,則該屬性必須爲讀寫屬性。
使用反射,可檢索用自定義特性定義的信息。主要方法是 GetCustomAttributes,它返回對象數組,這些對象在運行時等效於源代碼特性。
1 /// <summary> 2 /// 角色特性 3 /// </summary> 4 /// RoleAttribute:特性的名稱,繼承 Attribute,爲自定義特性 5 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)] 6 public class RoleAttribute : Attribute 7 { 8 private string _name; 9 /// <summary> 10 /// 啓用標識 11 /// </summary> 12 public bool IsEnable { get; set; } 13 14 /// <summary> 15 /// 構造函數 16 /// </summary> 17 /// <param name="name"></param> 18 public RoleAttribute(string name) 19 { 20 _name = name; 21 } 22 }
1 [Role("Me", IsEnable = true)] 2 public class OurClass 3 { 4 //... ... 5 }
概念上等效於
1 RoleAttribute role = new RoleAttribute("Me"); 2 role.IsEnable = true;
可是,直到查詢 OurClass 來獲取特性後纔會執行此代碼。對 OurClass 調用 GetCustomAttributes 會致使按上述方式構造並初始化一個 RoleAttribute 對象。若是該類具備其餘特性,則按類似的方式構造其餘特性對象。而後 GetCustomAttributes 返回 RoleAttribute 對象和數組中的任何其餘特性對象。以後就能夠對此數組進行迭代,肯定根據每一個數組元素的類型所應用的特性,並從特性對象中提取信息。
這裏,定義一個自定義特性,將其應用於若干實體並經過反射進行檢索。
1 /// <summary> 2 /// 角色特性 3 /// </summary> 4 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = true)] 5 public class RoleAttribute : Attribute 6 { 7 private readonly string _name; 8 9 /// <summary> 10 /// 啓用標識 11 /// </summary> 12 public bool IsEnable { get; set; } 13 14 /// <summary> 15 /// 構造函數 16 /// </summary> 17 /// <param name="name"></param> 18 public RoleAttribute(string name) 19 { 20 _name = name; 21 } 22 23 public string GetName() 24 { 25 return _name; 26 } 27 }
class MyClass1 { } [Role("Me")] class MyClass2 { } [Role("Me"), Role("You", IsEnable = true)] class MyClass3 { }
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 Output(typeof(MyClass1)); 6 Output(typeof(MyClass2)); 7 Output(typeof(MyClass3)); 8 9 Console.Read(); 10 } 11 12 /// <summary> 13 /// 輸出 14 /// </summary> 15 /// <param name="t"></param> 16 static void Output(Type t) 17 { 18 Console.WriteLine($"Class: {t}"); 19 20 var attributes = t.GetCustomAttributes(); 21 foreach (var attribute in attributes) 22 { 23 var attr = attribute as RoleAttribute; 24 25 if (attr == null) 26 { 27 return; 28 } 29 30 Console.WriteLine($" Name: {attr.GetName()}, IsEnable: {attr.IsEnable}"); 31 } 32 } 33 }
《只是想簡單說下表達式樹 - Expression Trees》
【參考】微軟官方文檔