首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C# 語言規范解讀:屬性 —— 聲明性元數據的擴展機制

     編輯:關於C語言

前言:從命令式到聲明式

C# 是命令式語言,但通過屬性 實現聲明性編程擴展:程序員定義屬性類型,附加到程序實體 (類、方法、屬性、參數、程序集等),運行時通過反射檢索。本文對應規范第 1.14 節 (及第 17 章)「屬性」。

屬性定義:本質是類


using System;

[AttributeUsage(AttributeTargets.All)] // 指定可應用目標
public class HelpAttribute : Attribute
{
    // 位置參數:構造函數參數
    public HelpAttribute(string url)
    {
        this.Url = url;
    }

    // 命名參數:公共非靜態字段/屬性
    public string Topic = null; // 字段形式
    public string Url { get; }  // 唯讀屬性形式

    // 可添加其他實例字段/屬性/方法
}

關鍵點

  • 繼承 `System.Attribute`
  • `[AttributeUsage]` 指定合法目標 (`Class`, `Method`, `Property`, `Parameter`, `Assembly`, `Module`, `All` 等),可組合 (`AttributeTargets.Class | AttributeTargets.Method`)
  • 位置參數 = 構造函數參數 (必填)
  • 命名參數 = 可讀寫公共實例字段/屬性 (選填,按名賦值)
  • 類名慣例以 `Attribute` 結尾,使用時可省略 (如 `[Help(...)]` 等同 `[HelpAttribute(...)]`)

屬性應用示例


// 類級別:僅位置參數
[Help("https://docs.example.com/Class1.htm")]
public class Class1
{
    // 方法級別:位置參數 + 命名參數
    [Help("https://docs.example.com/Class1.htm", Topic = "F")]
    public void F() { }
}

運行時檢索:反射 API


using System;

class Test
{
    static void Main()
    {
        Type type = typeof(Class1);
        
        // 獲取類級別屬性 (inherit=true 搜尋繼承鏈)
        object[] attrs = type.GetCustomAttributes(typeof(HelpAttribute), true);
        
        if (attrs.Length == 0)
        {
            Console.WriteLine("Class1 has no Help attribute.");
        }
        else
        {
            HelpAttribute ha = (HelpAttribute)attrs[0];
            Console.WriteLine($"Url = {ha.Url}, Topic = {ha.Topic}");
        }
        
        // 獲取方法級別屬性
        var method = type.GetMethod("F");
        var methodAttrs = method.GetCustomAttributes(true);
        foreach (var ma in methodAttrs)
            Console.WriteLine($"Method F: Url={ma.Url}, Topic={ma.Topic}");
    }
}

核心反射 API 速查

API用途關鍵參數
`MemberInfo.GetCustomAttributes(Type, inherit)`獲取指定類型屬性 (返回 object[])`inherit`: 是否搜尋繼承鏈
`MemberInfo.GetCustomAttributes(inherit)`泛型版本,直接返回 T[]同上
`Attribute.GetCustomAttribute(MemberInfo, Type)`靜態方法,獲取單個 (多個拋異常)
`Attribute.IsDefined(MemberInfo, Type, inherit)`快速判斷是否應用
`Assembly.GetCustomAttributes()`程序集級屬性 (如 AssemblyVersion, AssemblyTitle)

內置常用屬性

  • `[Obsolete("message", true)]`:標記過時,`true`=編譯錯誤,`false`=警告
  • `[Conditional("DEBUG")]`:僅在定義符號時編譯調用 (調試輸出必備)
  • `[Serializable]`:標記可序列化
  • `[DllImport("dll")]`:P/Invoke 簽名
  • `[CLSCompliant(true)]`:語言獨立性檢查
  • `[CallerMemberName]`, `[CallerFilePath]`, `[CallerLineNumber]`:自動獲取調用者信息 (參數默認值)

進階:多次應用與屬性繼承


// AllowMultiple=true 允許同一目標多次應用
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class AuthorAttribute : Attribute
{
    public string Name { get; }
    public AuthorAttribute(string name) => Name = name;
}

[Author("Alice")]
[Author("Bob")] // 合法
public class MyClass { }

最佳實踐

  1. 密封屬性類:`public sealed class MyAttribute : Attribute` 避免繼承複雜性
  2. 僅構造函數參數為位置參數,屬性/字段為命名參數
  3. 屬性應輕量:避免複雜邏輯、IO、靜態狀態,僅作為元數據標記
  4. 運行時檢索緩存:反射有開銷,熱路徑緩存 `GetCustomAttributes` 結果
  5. 文檔化目標約束:`AttributeUsage` 明確標註合法目標,防止誤用

總結

屬性是 C# 類型系統的「標籤系統」,將聲明性元數據與代碼結構綁定,運行時通過反射賦能:序列化控制、驗證規則、依賴注入標記、代碼生成指令、測試框架發現、編譯器指導 等無數框架機制的基石。掌握 `AttributeUsage`、位置/命名參數、反射檢索、內置屬性、密封設計,是從「使用框架」走向「構建框架」的關鍵能力。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved