.NET 開發基金會/屬性
| .NET 開發基金會 | |
|---|---|
此頁面原始文字由 William "Scott" Baker 撰寫
屬性是一種“標記”程式碼元資料元素的方法,使用描述性資訊,這些資訊可以在執行時使用反射訪問。屬性必須直接或間接派生自System.Attribute。.NET 框架中存在大量屬性;您也可以定義自己的屬性。在程式碼中使用屬性有三個方面
- 定義自定義屬性類,包括以下步驟
- 將AttributeUsageAttribute屬性分配給您的類。
- 編寫程式碼來定義您的自定義屬性類。
- 為您的類建立引數。
- 將屬性分配給程式碼成員。
- 在執行時檢索屬性資訊。
如前所述,.NET 框架中存在多個預定義屬性;您可能已經在程式碼中使用過它們。特別是 XML 解析器在(反)序列化物件時嚴重依賴屬性。您也可以定義自己的自定義屬性,如下所示。定義自定義屬性包括三個步驟
- 將AttributeUsageAttribute屬性分配給您的類。
- 編寫程式碼來定義您的自定義屬性類。
- 為您的類建立引數。
注意:在 Visual Basic 中,必須在所有自定義屬性上使用AttributeUsageAttribute屬性。將AttributeUsageAttribute屬性應用於類
[AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = false)]
public Class QualityCheckAttribute : System.Attribute
{
// ...
}
請注意使用“AttributeUsage”與“AttributeUsageAttribute”。按照慣例,所有屬性都以“Attribute”字尾命名 - 但在程式碼中使用時可以省略字尾。這對於使用者定義的屬性也是如此;QualityCheckAttribute屬性可以引用為
[QualityCheck] // or... [QualityCheckAttribute]
該AttributeUsageAttribute具有三個成員ValidOn、AllowMultiple和Inherited.
- 該ValidOn成員接受AttributeTargets列舉值,並將您的屬性限制為您指定的程式碼型別。預設值為AttributeTargets.All。您可以將您的屬性限制為類、列舉、返回值或以下列表中的任何內容
All (any element) Delegate GenericParameter Parameter Assembly Enum Interface Property Class Event Method ReturnValue Constructor Field Module* Struct *Module refers to a portable executable (.exe or .dll), and not a Visual Basic standard module.
您還可以將目標型別組合為按位 OR 運算以指定多個可接受值
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
- AllowMultiple是一個布林值,它確定是否可以將屬性多次應用於給定成員。預設值為false。以下示例說明了在程式碼元素上使用多個相同屬性的例項
[QualityCheck("Scott Baker", "02/28/06", IsApproved = true,
Comment = "This code follows all established guidelines. Release approved.")]
[QualityCheck("Matt Kauffman", "01/15/06", IsApproved = false,
Comment = "Code quality much improved. Minor revision required.")]
[QualityCheck("Joe Schmoe", 01/01/06", IsApproved = false,
Comment = "This code is a mess and needs a complete rewrite")]
public class MyClass
{
// ...
}
- 該Inherited成員確定是否將類上設定的屬性繼承到繼承樹中的更低級別類。預設值為 true
[AttributeUsage(AttributeTargets.Class)]
public class AttrOneAttribute : Attribute
{
// ...
}
// This attribute will not be inherited
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class AttrTwoAttribute : Attribute
{
// ...
}
[AttrOne]
[AttrTwo]
public class ClassOne
{
// ...
}
// This class inherits AttrOne from ClassOne,
// but not AttrTwo
public class ClassTwo : ClassOne
{
// ...
}
- 屬性是繼承自System.Attribute的類,直接或間接繼承
public Class QualityCheckAttribute : System.Attribute // direct
{
// ...
}
public Class FinalCheck : QualityCheckAttribute // indirect
{
// ...
}
- 屬性類具有AttributeUsageAttribute屬性
[AttributeUsage(AllowMultiple = true, Inherited = false)]
public Class QualityCheckAttribute : System.Attribute
{
// ...
}
如前所述,在 VB 中必須使用AttributeUsageAttribute。在 C# 中,如果未宣告,則會自動應用預設值。
public class QualityCheckAttribute : Attribute
{
public QualityCheckAttribute(string Name, string Date)
// ...
}
public class QualityCheckAttribute : Attribute
{
private string _name;
private string _date;
private bool isApproved;
public bool IsApproved
{
get {return isApproved;}
set {isApproved = value;}
}
public QualityCheckAttribute(string Name, string Date)
{
// ...
}
}
請記住,程式碼中的變數可以既是位置引數又是命名引數。如果我們要為 _name 和 _date 欄位新增公共屬性,我們可以將它們用作命名引數或位置引數。當然,這並不推薦:必需引數應為位置引數,可選引數應為命名引數。
您已經看到了將屬性分配給程式碼成員的示例。但是,需要澄清一些要點。
- 消歧義是指在程式碼成員上使用屬性的澄清。
- 語法 - 應用多個屬性的方法不止一種。
public class MyAttribute : Attribute
{
[SomeAttribute("Hello")]
public string MyMethod(aString)
{
return aString;
}
}
消除歧義可以解決這些問題。透過指定屬性應用到的程式碼型別,我們可以消除混淆。以下程式碼顯示屬性應用於返回值值
public class MyAttribute : Attribute
{
[return : SomeAttribute]
public string MyMethod(aString)
{
return aString;
}
}
下表列出了所有允許使用屬性的宣告;對於每個宣告,第二列列出了宣告中屬性的可能目標。**粗體**的目標是預設目標。
Declaration Possible targets assembly assembly module module class type struct type interface type enum type delegate type, return method method, return parameter param field field property — indexer property property — get accessor method, return property — set accessor method, param, return event — field event, field, method event — property event, property event — add method, param event — remove method, param *Reference: Disambiguating Attribute Targets (C# Programming Guide)
人們可能會認為AttributeUsageAttribute 的 AttributeTargets在屬性定義中將有助於防止這種混淆:這是錯誤的。編譯器在解決衝突時不會使用AttributeUsageAttribute資訊。即使您定義了一個屬性使其僅應用於特定型別,例如AttributeTargets.Return,您仍然必須明確它應用於返回值型別,在應用屬性時,否則編譯器將使用預設目標方法型別,並丟擲錯誤。
[AttrOne(...), AttrTwo(...)] // or... [AttrOne(...)] [AttrTwo(...)]
這兩種方法是等效的。請記住,如果您要在單個大括號中指定多個屬性,則它們必須應用於相同的目標型別。否則,您必須為每種型別提供單獨的宣告
[return : AttrOne(...), method : AttrTwo(...)] // <-- invalid! // instead, you must... [return : AttrOne(...)] [method : AttrTwo(...)]
能夠宣告和應用屬性,除非我們能夠檢索這些資料並使用它們,否則並不是很有幫助。幸運的是,這是一個簡單的過程。將解決三個基本場景
- 從成員中檢索單個屬性。
- 從成員中檢索多個屬性。
- 從多個成員中檢索單個型別的屬性。
要訪問屬性資訊
- 宣告屬性型別的例項。
- 使用Attribute.GetCustomAttribute(型別, typeof)方法將屬性讀入例項。
- 使用例項的屬性來讀取值。
以下示例程式碼聲明瞭一個類ExampleClass具有一個QualityCheck屬性。該GetSingleAttribute方法接受目標成員型別和要查詢的屬性型別。該Attribute.GetCustomAttribute方法將屬性資訊檢索到attr物件中,從中我們可以讀取最重要的IsApproved屬性
[QualityCheck("Scott Baker", "02/04/2006", IsApproved = false)]
public class ExampleClass
{
public static void Main()
{
GetSingleAttribute(typeof(ExampleClass), typeof(QualityCheck))
}
public static void GetSingleAttribute(Type targetType, Type attrType)
{
typeof(attrType) attr = (attrType)Attribute.GetCustomAttribute(targetType, typeof(attrType));
if (attr == null)
{ //... }
else
{
Console.Writeline(attr.IsApproved);
}
}
要記住的一個重要因素是GetCustomAttribute方法設計為僅讀取**一個**屬性。GetCustomAttribute實際上檢查是否有多個屬性匹配 - 如果沒有匹配,它將返回null,但如果有多個匹配,它將丟擲AmbiguousMatchException。在檢查屬性時,唯一可以安全地使用GetCustomAttribute的是當屬性的定義宣告[AttributeUsage(AllowMultiple=false)].
讀取成員上的多個屬性例項與讀取一個屬性例項並沒有太大區別;要讀取多個屬性,請使用複數GetCustomAttributes,它返回一個屬性陣列。然後,您可以遍歷生成的陣列並讀取值
QualityCheck[] attrArray = (QualityCheck[])Attribute.GetCustomAttributes(t, typeof(QualityCheck));
foreach (QualityCheck attr in attrArray)
{
Console.Writeline(attr.IsApproved);
}
如果要執行更復雜的操作,例如檢查類中的每個方法是否具有 QualityCheck 屬性,該怎麼辦?由於System.Reflection名稱空間,我們甚至不需要費力。只需將所有成員(在本例中為方法)讀入MemberInfo陣列並遍歷它們
using System.Reflection
public class ExampleClass
{
public static void Main()
{
RetrieveAttributes(typeof(ExampleClass));
}
public void RetrieveAttributes(Type t)
{
MemberInfo[] methodList = t.GetMethods();
foreach (MemberInfo m in methodList)
{
QualityCheck[] attrArray = (QualityCheck[])Attribute.GetCustomAttributes(m, typeof(QualityCheck));
foreach (QualityCheck attr in attrArray)
{
Console.Writeline(attr.IsApproved);
}
}
}
}