C# 特性(Attribute)
特性(Attribute)是用於在執行時間傳遞程式中各種元素(例如類別、方法、結構、枚舉、元件等)的行為資訊的聲明性標籤。您可以透過使用特性為程式新增聲明性資訊。一個聲明性標籤是透過放置在它所應用的元素前面的方括號([ ])來描述的。
特性(Attribute)用於新增元數據,如編譯器指令和註解、描述、方法、類別等其他資訊。 .Net 框架提供了兩種類型的特性:預先定義特性和自訂特性。
規定特性(Attribute)
規定特性(Attribute)的語法如下:
[attribute(positional_parameters, name_parameter = value, ...)] element
特性(Attribute)的名稱和值是在方括號內規定的,放置在它所在應用的元素之前。 positional_parameters 規定必需的訊息,name_parameter 規定可選的資訊。
預先定義特性(Attribute)
.Net 框架提供了三種預定義特性:
AttributeUsage
-
#Conditional
Obsolete
AttributeUsage
##預設特性AttributeUsage 描述如何使用一個自訂特性類別。它規定了特性可應用到的項目的類型。
規定此特性的語法如下:[AttributeUsage( validon, AllowMultiple=allowmultiple, Inherited=inherited )]其中:
- #參數 validon 規定特性可被放置的語言元素。它是枚舉器
AttributeTargets 的值的組合。預設值是 AttributeTargets.All。
- 參數
allowmultiple(可選的)為該特性的 AllowMultiple 屬性(property)提供一個布林值。如果為 true,則該特性是多用的。預設值是 false(單用的)。
- 參數
inherited(可選的)為該特性的 Inherited 屬性(property)提供一個布林值。如果為 true,則該特性可被衍生類別繼承。預設值是 false(不被繼承)。
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Constructor | AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = true)]Conditional這個預定義特性標記了一個條件方法,執行依賴它頂的預處理標識符。 它會造成方法呼叫的條件編譯,取決於指定的值,例如
Debug 或 Trace。例如,當偵錯程式碼時顯示變數的值。
規定該特性的語法如下:[Conditional( conditionalSymbol )]例如:
[Conditional("DEBUG")]下面的實例示範了該特性:
#define DEBUG using System; using System.Diagnostics; public class Myclass { [Conditional("DEBUG")] public static void Message(string msg) { Console.WriteLine(msg); } } class Test { static void function1() { Myclass.Message("In Function 1."); function2(); } static void function2() { Myclass.Message("In Function 2."); } public static void Main() { Myclass.Message("In Main function."); function1(); Console.ReadKey(); } }當上面的程式碼被編譯和執行時,它會產生下列結果:
In Main function In Function 1 In Function 2Obsolete這個預定義特性標記了不應該被使用的程式實體。它可以讓您通知編譯器丟棄某個特定的目標元素。例如,當一個新方法被用在一個類別中,但是您仍然想要保持類別中的舊方法,您可以透過顯示一個應該使用新方法,而不是舊方法的訊息,來標記它為obsolete(過時的)。 規定該特性的語法如下:
[Obsolete( message )] [Obsolete( message, iserror )]其中:
- #參數
message,是字串,描述項目為什麼過時的原因以及該替代使用什麼。
- 參數
iserror,是一個布林值。如果該值為 true,編譯器應把該專案的使用當作一個錯誤。預設值是 false(編譯器產生一個警告)。
using System; public class MyClass { [Obsolete("Don't use OldMethod, use NewMethod instead", true)] static void OldMethod() { Console.WriteLine("It is the old method"); } static void NewMethod() { Console.WriteLine("It is the new method"); } public static void Main() { OldMethod(); } }當您嘗試編譯程式時,編譯器會給予錯誤訊息說明:
Don't use OldMethod, use NewMethod instead
建立自訂特性(Attribute)
.Net 框架允許建立自訂特性,用於儲存聲明性的信息,且可在執行時擷取。此資訊根據設計標準和應用程式需要,可與任何目標元素相關。
建立並使用自訂特性包含四個步驟:
聲明自訂特性
建立自訂特性
在目標程式元素上應用自訂特性
#透過反射存取特性
最後一個步驟包含編寫一個簡單的程式來讀取元資料以便查找各種符號。元資料是用來描述其他資料的資料和資訊。該程式應使用反射來在運行時存取特性。我們將在下一章詳細討論這一點。
宣告自訂特性
一個新的自訂特性應衍生自 System.Attribute 類別。例如:
// 一个自定义特性 BugFix 被赋给类及其成员 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Constructor | AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = true)] public class DeBugInfo : System.Attribute
在上面的程式碼中,我們已經宣告了一個名為 DeBugInfo 的自訂特性。
建立自訂特性
讓我們建立一個名為 DeBugInfo 的自訂特性,將儲存偵錯程式所獲得的資訊。它儲存下面的資訊:
bug 的程式碼編號
#辨識該bug 的開發人員名稱
-
最後一次檢視該程式碼的日期
一個儲存了開發人員標記的字串訊息
我們的DeBugInfo 類別將帶有三個用於儲存前三個資訊的私有屬性(property)和一個用於儲存訊息的公有屬性(property)。所以 bug 編號、開發人員名字和審查日期將是 DeBugInfo 類別的必需的定位( positional)參數,訊息將是一個可選的命名(named)參數。
每個特性必須至少有一個建構子。必需的定位( positional)參數應透過建構函數傳遞。下面的程式碼示範了DeBugInfo 類別:
// 一个自定义特性 BugFix 被赋给类及其成员 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Constructor | AttributeTargets.Field | AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = true)] public class DeBugInfo : System.Attribute { private int bugNo; private string developer; private string lastReview; public string message; public DeBugInfo(int bg, string dev, string d) { this.bugNo = bg; this.developer = dev; this.lastReview = d; } public int BugNo { get { return bugNo; } } public string Developer { get { return developer; } } public string LastReview { get { return lastReview; } } public string Message { get { return message; } set { message = value; } } }
應用自訂特性
透過把特性放置在緊接著它的目標之前,來應用該特性:
[DeBugInfo(45, "Zara Ali", "12/8/2012", Message = "Return type mismatch")] [DeBugInfo(49, "Nuha Ali", "10/10/2012", Message = "Unused variable")] class Rectangle { // 成员变量 protected double length; protected double width; public Rectangle(double l, double w) { length = l; width = w; } [DeBugInfo(55, "Zara Ali", "19/10/2012", Message = "Return type mismatch")] public double GetArea() { return length * width; } [DeBugInfo(56, "Zara Ali", "19/10/2012")] public void Display() { Console.WriteLine("Length: {0}", length); Console.WriteLine("Width: {0}", width); Console.WriteLine("Area: {0}", GetArea()); } }
在下一章中,我們將使用Reflection 類別物件來檢索這些資訊。