C#Attribute(特性)的定義與使用

1.什么是 Attribute

1.1 定義
Attribute 是一種“聲明式元數據(declarative metadata)”機制。
? 附加位置:程序集、模塊、類型、字段、屬性、方法、方法參數、方法返回值、事件、泛型參數、局部變量、本地函數、Lambda 表達式、甚至表達式樹。
? 本質:編譯器把特性的實例化信息序列化到元數據表中;運行期可通過反射讀取,或供編譯器、分析器、Source Generator 消費。

? 語法:用方括號 [...] 寫在目標實體前面,可簡寫、組合、帶命名參數。

1.2 與注釋/ XML 的區別

? 注釋不參與編譯,XML 文檔只在 IntelliSense 中可見;而 Attribute 是“可編譯、可反射”的元數據。
? 因此 Attribute 可驅動“代碼生成”、“運行期行為”或“編譯期驗證”。

2. 內置 Attribute 全景圖

2.1 編譯器指令型

? [Obsolete]:產生警告或錯誤。

? [Conditional("DEBUG")]:方法調用在 Release 被編譯器擦除。
? [CallerMemberName] / [CallerFilePath] / [CallerLineNumber]:編譯期自動填充值。
? [GeneratedCode]:告訴工具“這是生成的代碼”。

2.2 CLR/JIT/Interop

? [DllImport][StructLayout][MarshalAs][UnmanagedCallersOnly][SuppressGCTransition]

2.3 序列化
? [Serializable][NonSerialized][DataContract]/[DataMember][JsonProperty] (System.Text.Json) 等。

2.4 安全
? [AllowNull][NotNull][SecurityCritical][SecuritySafeCritical]

2.5 反射/動態
? [Dynamic][Nullable][TupleElementNames](編譯器自動生成)。

2.6 ASP.NET Core / WCF / WinForms / EF / …
? [HttpGet][Authorize][ApiController][Table][Key][Display][Inject] 等。

2.7 代碼分析
? [NotNullWhen][DoesNotReturn][RequiresUnreferencedCode][RequiresDynamicCode]

2.8 實驗性 API
? [Experimental("DIAG_ID")].

3. AttributeUsage:如何限制自定義特性

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct,AllowMultiple = false,Inherited = true)]
public sealed class MySpecialAttribute : Attribute
{// ...
}

? AttributeTargets 枚舉是位標志,可疊加。

? AllowMultiple:同一目標是否允許重復貼多個。

? Inherited:派生類/重寫成員是否“繼承”該特性。注意:僅對“類、方法、屬性、事件、字段”有效;接口、返回值、參數不會被繼承。

? Inherited = false 時,派生類若想保留需重新寫一次。

4. Attribute 構造函數與命名參數

4.1 定位參數(positional)
只能出現在構造函數實參列表,順序必須一致。

4.2 命名參數(named)
必須是 public 非 static 字段或屬性,且類型只能是:

? 基本類型(含 string)
? Type
? object(必須是以上類型的常量表達式)
? 一維數組(元素類型同上)
不能是泛型、decimal、DateTime、可空值類型、動態、指針、用戶定義類型。

示例:

[MyAttr(42, Description = "Answer", Tags = new[] { "a", "b" })]

5. 運行期讀取:System.Reflection

5.1 傳統 API

var attrs = typeof(Foo).GetCustomAttributes(typeof(MySpecialAttribute), inherit: true);

? GetCustomAttributes:返回 object[];可指定繼承策略。

? IsDefined:僅判斷是否存在,性能更高。

? Attribute.GetCustomAttribute:返回單個 Attribute,存在多個時拋 AmbiguousMatchException。

5.2 .NET 4.5+ 的泛型版本

IEnumerable<MySpecialAttribute> attrs =typeof(Foo).GetCustomAttributes<MySpecialAttribute>(inherit: true);

5.3 性能陷阱
? 首次訪問元數據會觸發類型加載,反射本身有開銷。
? 多次反射同一特性可用靜態字段緩存:

static readonly MySpecialAttribute cache =Attribute.GetCustomAttribute(typeof(Foo), typeof(MySpecialAttribute)) as MySpecialAttribute;

6. 編譯期消費:Roslyn Analyzer & Source Generator

? 分析器通過 Compilation.GetSymbolsWithNameSemanticModel.GetDeclaredSymbol 等 API 讀取 Attribute 元數據,發出診斷。

? Source Generator 可掃描帶有特定 Attribute 的類,然后生成額外源文件(如注冊表、代理、序列化器)。

context.SyntaxProvider.CreateSyntaxProvider(predicate: (node, _) => node is ClassDeclarationSyntax,transform: (ctx, _) => ctx.SemanticModel.GetDeclaredSymbol(ctx.Node)).Where(symbol => symbol.GetAttributes().Any(a => a.AttributeClass.Name == "AutoRegisterAttribute"))

7. 預定義 Attribute 的“隱藏行為”

7.1 [Serializable]
在元數據中設置 TypeAttributes.Serializable 標志,供 BinaryFormatter/SoapFormatter 使用。

7.2 [MethodImpl(MethodImplOptions.AggressiveInlining)]
直接指導 JIT,而非反射;所以反射拿不到它。

7.3 [CallerMemberName]
編譯器在調用點把字符串常量寫進 IL,運行期無需反射。

7.4 [UnsafeAccessor] (.NET 8 preview)
通過 JIT 內部鉤子繞過可訪問性檢查。

8. Attribute 與 AOP(面向切面編程)

? PostSharp、AspectInjector、Castle DynamicProxy、Metalama 等框架:
編譯期或運行期掃描特性 → 編織 IL/生成代理 → 執行攔截邏輯。

[LogCall] // 自定義 Attribute
public void Foo() { }

運行期代理重寫為:

public void Foo()
{Logger.LogEnter();try { original(); }finally { Logger.LogExit(); }
}

9. 條件編譯與 Attribute

? [Conditional("DEBUG")] 僅影響調用點,不影響特性本身。
? 若想特性本身僅在 DEBUG 存在,需要:

#if DEBUG
[SomeDebugOnlyAttr]
#endif
public void Foo() { }

10. Attribute 與 Nullable Reference Type

? [AllowNull][DisallowNull][MaybeNull][NotNull] 等配合可空性分析。
? 編譯器利用這些特性改進流分析,不會產生運行時 IL。

11. 泛型與 Attribute

11.1 泛型類型/方法
可以貼特性,如 [JsonConverter(typeof(MyConv<>))]
但特性類本身不能是泛型(CLI 限制)。

11.2 泛型參數特性

class Foo<[MyConstraint] T> { }

AttributeTargets.GenericParameter,且只能使用一次。

12. 局部變量 & Lambda

C# 8 起可在局部變量、本地函數、Lambda 參數上使用 [NotNull][EnumeratorCancellation] 等。

void M([EnumeratorCancellation] CancellationToken token) { }

13. Attribute 與記錄類型

? record/record struct 本質仍是類/結構,常規貼法即可。
? [property: Required] 用于 record 的 init-only 屬性。

14. 模塊級與程序集級 Attribute

[assembly: AssemblyVersion("1.2.3.4")]
[assembly: InternalsVisibleTo("My.Tests")]
[module: UnverifiableCode]

? 必須放在文件頂層(namespace 之外)。
? module: 前綴表示作用于模塊(很少用)。

15. 特性命名約定

? 類名必須以 Attribute 結尾;使用時可以省略。
? 若同時存在 MySpecial 和 MySpecialAttribute,編譯器優先匹配后綴。

16. CLS 兼容性

? 公開可見的自定義 Attribute 需滿足 CLS:構造函數和公共字段/屬性類型必須 CLS 兼容。
? 用 [assembly: CLSCompliant(true)] 強制檢查。

17. 自定義 Attribute 的“實例化”過程

  1. 編譯器遇到 [MyAttr(123)]

  2. 生成元數據:
    ? 指向 MyAttrAttribute 的 TypeRef/TypeDef token
    ? 構造函數的 MethodRef token
    ? 定位參數 blob(123)

  3. 運行期 GetCustomAttributes 時:
    ? CLR 分配 MyAttrAttribute 對象(通過無參或匹配構造函數)
    ? 設置字段/屬性
    ? 返回給用戶代碼
    注意:特性類必須具有 public 構造函數,且定位參數必須與構造函數匹配。

18. Attribute 繼承與接口

? Attribute 類本身可繼承(如 ValidationAttribute),但一個 Attribute 實例只能附加到單個目標。
? 接口不能貼 Attribute,但 [AttributeUsage(AttributeTargets.Interface)] 允許特性用于接口聲明本身。

19. 性能優化實戰

? 避免在熱路徑頻繁反射,可緩存 static readonly Attribute[]
? Source Generator 在編譯期生成靜態表,實現“零反射”。
? 使用 IsDefined 代替 GetCustomAttributes 做布爾判斷。
? 在 NativeAOT 中,使用 [DynamicDependency] 或 rd.xml 防止特性被裁剪。

20. 調試技巧

? VS 的“模塊”窗口可查看元數據 token。
? ildasm /metadata 查看 CustomAttribute 表。
? dotnet-dump SOS:!dumpmd, !dumpil 可驗證特性是否寫入。
? 使用 System.Reflection.Metadata 輕量級讀取元數據無需加載類型。

21. 常見陷阱

  1. AllowMultiple = false 卻重復貼 → 編譯錯誤 CS0579。

  2. 構造函數參數類型與實參不符 → 編譯錯誤 CS0182。

  3. 特性類自身未繼承 System.Attribute → 編譯錯誤 CS0616。

  4. 繼承鏈忘記設置 Inherited = true 導致派生類缺失。

  5. 在 NativeAOT/ILLinker 中忘記根特性 → 運行期 MissingMetadataException

  6. 在 partial 類文件中重復貼程序集級 Attribute → 用 extern alias#if 避免。

完整自定義 Attribute 模板

[AttributeUsage(AttributeTargets.Class |AttributeTargets.Method |AttributeTargets.Property,AllowMultiple = true,Inherited = true)]
public sealed class RetryAttribute : Attribute
{// 定位參數public RetryAttribute(int maxRetries){MaxRetries = maxRetries;}public int MaxRetries { get; }// 命名參數public int DelayMilliseconds { get; set; } = 1000;public Type[] ExceptionTypes { get; set; } = Array.Empty<Type>();
}

使用:

[Retry(3, DelayMilliseconds = 500, ExceptionTypes = new[] { typeof(TimeoutException) })]
public async Task<HttpResponseMessage> CallApiAsync() { ... }

消費:

var method = typeof(MyService).GetMethod(nameof(MyService.CallApiAsync))!;
var retry = method.GetCustomAttribute<RetryAttribute>()!;
Console.WriteLine(retry.MaxRetries);

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/92568.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/92568.shtml
英文地址,請注明出處:http://en.pswp.cn/web/92568.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

飛書對接E簽寶完整方案

1、概述飛書和E簽寶在各自領域都屬于領先的產品&#xff0c;但因為E簽寶與釘釘的特殊關系&#xff0c;一直以來E簽寶都只實現了與釘釘的深度集成&#xff0c;一家企業如果想同時使用飛書和E簽寶&#xff0c;則需要通過S-HUB這樣的產品來進行橋接&#xff0c;用戶在飛書端審批&a…

Mysql快速導出數據庫設計說明書word文檔(表結構、類型、注釋、是否有主鍵)

主要有三種方式&#xff0c;根據你的需求來選擇&#xff1a; 1、Mysql語句查詢 優點&#xff1a;無需安裝額外的軟件&#xff0c;使用你常用的數據庫可視化工具即可 缺點&#xff1a;受限于你的數據庫可視化工具的導出功能&#xff0c;需要額外寫腳本進行處理 2、Python腳本…

DigitalProductId解密算法php調試版piddebug.php

<?php // 使用數組字面量 $digits [B, C, D, F, G, H, J, K, M, P, Q, R,T, V, W, X, Y, 2, 3, 4, 6, 7, 8, 9]; //foreach ($digits as $digit) { // echo $digit."<br>"; //}$hexPidarray(0xc2,0x49,0x4b,0xcc,0x60,0x34,0x09,0xcd,0x96,0xf7,0xec,0…

IDEA快捷鍵壁紙分享

說明&#xff08;1&#xff09;因為顯示器的尺寸不同&#xff0c;對快捷鍵顯示的位置稍作調整 &#xff08;2&#xff09;這里默認您熟悉常用的快捷鍵&#xff0c;分享一些功能好用但是用的少的快捷鍵空壁紙筆記本壁紙&#xff08;15.6寸&#xff09;24 寸顯示器壁紙

InnoDB vs MyISAM: MySQL存儲引擎的世紀對決

選錯存儲引擎&#xff1f;你的數據庫性能可能暴跌80%&#xff01; 本文用最直觀的對比拆解MySQL兩大核心存儲引擎的差異&#xff0c;讓你徹底明白什么場景該選誰。一、引擎全景圖: 數據庫的"心臟"之爭 ?? #mermaid-svg-KTQko8kEUvOkTb4L {font-family:"trebuc…

【Avalonia】無開發者賬號使用iOS真機調試跨平臺應用

文章目錄1. 要求1.1 無需Apple開發者賬號1.2 最新版mac系統1.3 最新版Xcode2. 配對Mac3. 配置開發證書3.1 創建一個名為MTClient的Xcode項目3.2 找到簽名證書3.3 配置簽名3.4 配置標識符4. 真機調試4.1 設置應用首屏 Launch Screen4.2 設置應用圖標5. 問題5.1 DI異常該問題的解…

【LLM實戰|langchain】langchain基礎

every blog every motto: You can do more than you think. https://blog.csdn.net/weixin_39190382?typeblog 0. 前言 【LLM實戰|langchain】langchain基礎 1. 模型 I/O 封裝 把不同的模型&#xff0c;統一封裝成一個接口&#xff0c;方便更換模型而不用重構代碼。 1.1 …

十九、MySQL-DQL-基本查詢

基本查詢代碼&#xff1a;DQL:基本查詢 1.查詢指定字段 name,entrydate 并返回 -- 1.查詢指定字段 name,entrydate 并返回 select name,entrydate from tb_emp;2.查詢返回所有字段 -- 2.查詢返回所有字段 -- 推薦 select id, username, password, name, gender, image, job, e…

CamX-驍龍相機修改

1. 修改視頻模式預覽尺寸和分辨率 vendor/codeaurora/packages/apps/SnapdragonCamera/src/com/android/camera/CaptureModule.javaprivate void updatePreviewSize() {int width mPreviewSize.getWidth();int height mPreviewSize.getHeight(); - mPreviewSize new …

容器技術基礎與實踐:從鏡像管理到自動運行配置全攻略

1. 相比較虛擬機&#xff0c;容器有哪些技術優勢&#xff1f;&#xff08;1&#xff09;直接在操作系統上運行&#xff0c;從而跨系統上的所有容器共享資源&#xff0c;‘&#xff08;2&#xff09;共享主機的內核。&#xff08;3&#xff09;與虛擬機相比&#xff0c;它需要的…

書生浦語第五期-L1G4-InternLM 論文分類微調實踐(XTuner 版)

XTuner介紹一句話介紹XTuner&#xff1a;XTuner 是一個高效、靈活、全能的輕量化大模型微調工具庫。核心特點&#xff1a;高效&#xff1a;支持在有限資源下微調大模型&#xff0c;如在8GB顯存上微調7B參數模型&#xff0c;也支持多節點微調70B模型&#xff1b;自動分發高性能算…

從靈感枯竭到批量產出:無憂秘書創作平臺如何重構內容生產者的工作流程?全環節賦能分析

在當今快節奏的數字時代&#xff0c;內容創作者面臨著前所未有的挑戰。無論是自媒體運營者、自由撰稿人還是企業營銷人員&#xff0c;都需要高效地生產高質量的內容以滿足市場需求。然而&#xff0c;靈感枯竭、效率低下以及內容質量不穩定等問題常常困擾著這些內容生產者。為了…

【開源工具】基于Python的PDF清晰度增強工具全解析(附完整源碼)

??? 【開源工具】基于Python的PDF清晰度增強工具全解析(附完整源碼) ?? 個人主頁:創客白澤 - CSDN博客 ?? 系列專欄:??《Python開源項目實戰》 ?? 熱愛不止于代碼,熱情源自每一個靈感閃現的夜晚。愿以開源之火,點亮前行之路。 ?? 希望大家多多支持,我們一起進…

Qwen-Image開源模型實戰

Qwen-Image開源模型實戰&#xff1a;ComfyUI低顯存量化部署與中文海報生成指南 阿里云通義千問團隊最新開源的Qwen-Image模型以其卓越的中英文文本渲染能力在AI繪圖領域掀起了一場革命。這款200億參數的MMDiT架構模型不僅能夠生成高質量圖像&#xff0c;更突破了AI繪圖長期存在…

JavaWeb03——javascript基礎語法

1.什么是JavaScript&#xff1f;JavaScript&#xff08;簡稱 JS&#xff09;是一種 編程語言&#xff0c;它主要用來為網頁添加交互功能。它可以讓網頁變得動態&#xff0c;讓它不僅僅是靜態的文字和圖片&#xff0c;還能響應用戶操作&#xff08;比如點擊按鈕、彈框警告等&…

數據庫入門:從零開始構建你的第一個數據庫

歡迎來到數據庫的世界&#xff01;今天&#xff0c;我們將一起探索如何創建、管理和查詢數據庫。無論你是初學者還是希望加深理解的開發者&#xff0c;這篇博客都將幫助你更好地掌握數據庫的基礎知識。一、數據庫的基本操作創建數據庫首先&#xff0c;讓我們從創建一個新數據庫…

從匯編角度揭秘C++構造函數(1)

C的構造函數一直比較神秘&#xff0c;今天我們通過匯編的角度來揭秘一下&#xff0c;它的本質是什么。與常規函數有什么不同。從以下這段代碼說起&#xff1a; class Person { public:Person(int age) { _age age; }void printAge(){ printf("age %d\r\n",_age); …

java10學習筆記

Java 10 于 2018 年 3 月發布&#xff0c;是 Java 平臺按照新的六個月發布周期發布的第一個版本。雖然相比 Java 8 和 Java 9 的大型更新&#xff0c;Java 10 的變化較小&#xff0c;但仍然引入了一些重要的特性&#xff0c;特別是本地變量類型推斷&#xff08;var&#xff09;…

Flutter Listview的基本使用

Listview() 前端頁面常見的一個以列表方式顯示內容的組件。可垂直或水平滾動的列表。屬性說明scrollDirection設置滾動的方向&#xff0c;取值包括horizontal、verticalreverse設置是否翻轉&#xff0c;默認值falseitemExtent設置滾動方向子元素的長度&#xff0c;垂直方向為高…

強化學習筆記:從Q學習到GRPO

推薦學習huggingface的強化學習課程&#xff0c;全面了解強化學習的發展史。 以下是個人筆記&#xff0c;內容不一定完整&#xff0c;有些是個人理解。 基于值函數(value function)的強化學習 基于值函數(value function)的強化學習&#xff1a;學習的是一個值函數&#xff0…