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…