.NET 框架大量使用屬性實現宣告式程式設計,例如 [WebMethod] 將方法暴露為 Web Service、[Conditional("DEBUG")] 控制方法編譯。自訂屬性能將中繼資料嵌入組件,達成鬆耦合設計:程式碼與處理邏輯分離,執行時或設計時透過反射讀取屬性值,動態決定行為。
| 比較項目 | 自訂屬性 | 類別屬性 |
|---|---|---|
| 套用目標 | 組件、類別、方法、參數、欄位、介面等所有程式碼元素 | 僅限類別成員 |
| 值的來源 | 編譯時硬編碼於原始碼,編入中繼資料,唯讀 | 執行時動態賦值,可讀寫 |
| 實例關係 | 套用於類型定義,所有實例共享相同屬性值 | 屬於具體實例,每個實例值獨立 |
例如:[DefectTrack("1377", "12/15/02", "David")] 套用於類別,該類別所有實例共享此追蹤資訊;若用類別屬性則需每個實例個別設定。
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public class DefectTrackAttribute : Attribute
{
private string _defectID, _developerID, _origin, _fixComment;
private DateTime _modificationDate;
// 定位參數(必要)
public DefectTrackAttribute(string defectID, string modDate, string devID)
{
_defectID = defectID;
_modificationDate = DateTime.Parse(modDate);
_developerID = devID;
}
// 命名參數(選擇性,可讀寫)
public string Origin { get => _origin; set => _origin = value; }
public string FixComment { get => _fixComment; set => _fixComment = value; }
// 唯讀屬性對應定位參數
public string DefectID => _defectID;
public string ModificationDate => _modificationDate.ToShortDateString();
public string DeveloperID => _developerID;
}
關鍵點:
System.Attribute[AttributeUsage] 限定套用目標(AttributeTargets)、是否允許重複(AllowMultiple)、是否繼承(Inherited)[DefectTrack("1377", "12/15/02", "David Tansey")]
[DefectTrack("1363", "12/12/02", "Toni Feltman", Origin = "Coding: Unhandled Exception")]
public class SomeCustomPricingClass
{
[DefectTrack("1351", "12/10/02", "David Tansey",
Origin = "Specification: Missing Requirement",
FixComment = "Added PriceIsValid() function")]
public bool PriceIsValid(double price) => price > 0 && price < 1000;
}
優勢:屬性值編譯進組件,即使無原始碼也能透過反射讀取,優於傳統註解。
public static void DisplayDefectTrack(string assemblyName)
{
Assembly asm = Assembly.Load(assemblyName);
foreach (Type type in asm.GetTypes())
{
object[] attrs = type.GetCustomAttributes(typeof(DefectTrackAttribute), false);
foreach (DefectTrackAttribute attr in attrs)
{
Console.WriteLine($"Defect ID: {attr.DefectID}, Date: {attr.ModificationDate}");
}
// 同樣處理方法層級屬性
foreach (MethodInfo mi in type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly))
{
object[] mAttrs = mi.GetCustomAttributes(typeof(DefectTrackAttribute), false);
// ... 輸出屬性值
}
}
}
關鍵 API:GetCustomAttributes(Type, bool),第二個參數控制是否搜尋繼承鏈。
屬性不僅用於執行時,Visual Studio 設計工具也大量使用(如 [Category]、[Description] 控制屬性視窗顯示)。自訂屬性同樣能擴充設計時體驗,例如打造專屬程式碼產生器、驗證規則引擎等。
自訂屬性是 .NET 中繼資料系統的延伸,讓開發者能將領域知識直接編碼於程式結構中,配合反射實現高度彈性的架構。掌握 AttributeUsage、定位/命名參數、反射讀取三大核心,即可在專案中靈活應用。