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")]
下麵的實例演示了該特性:
實例
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 2
Obsolete
這個預定義特性標記了不應被使用的程式實體。它可以讓您通知編譯器丟棄某個特定的目標元素。例如,當一個新方法被用在一個類中,但是您仍然想要保持類中的舊方法,您可以通過顯示一個應該使用新方法,而不是舊方法的消息,來把它標記為 obsolete(過時的)。
規定該特性的語法如下:
[Obsolete( message )] [Obsolete( message, iserror )]
其中:
- 參數 message,是一個字串,描述專案為什麼過時以及該替代使用什麼。
- 參數 iserror,是一個布爾值。如果該值為 true,編譯器應把該專案的使用當作一個錯誤。默認值是 false(編譯器生成一個警告)。
下麵的實例演示了該特性:
實例
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 類:
實例
[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(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 類對象來檢索這些資訊。