讲师中心 微信公众号
AI工具推荐 视频效率加速

c# 如何自定义特性 attribute

老强君_6270

老强君_6270

发布时间:2026-01-07 11:28:02

|

1006人浏览过

|

来源于php中文网

原创

自定义 Attribute 是继承自 System.Attribute 的 public 非泛型非抽象类,需用 [AttributeUsage] 明确目标、允许多例及继承性,提供 public 构造函数(位置参数)和 public 属性(命名参数),并通过反射如 GetCustomAttribute() 读取。

c# 如何自定义特性 attribute

如何定义一个可被反射读取的自定义 Attribute 类

在 C# 中,自定义 Attribute 本质是一个继承自 System.Attribute 的类。它必须是公共的、非泛型、非抽象的,并且通常用 [AttributeUsage] 明确其适用范围和行为。

常见错误是忘记加 [AttributeUsage],导致编译通过但运行时无法通过反射获取;或把构造函数设为 private,让使用者无法实例化。

  • [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] —— 必须显式声明能修饰哪些目标(如类、方法)、是否允许多次使用、是否继承传递
  • 至少提供一个 public 构造函数(参数会成为“位置参数”,用于 [MyAttr("hello")] 这种写法)
  • 额外 public 属性可作为“命名参数”(如 [MyAttr("hello", Level = 2)])
  • 不要在构造函数里做耗时或可能抛异常的操作——特性实例化发生在编译期元数据写入阶段,运行时才读取,但构造逻辑仍需安全
using System;
<p>[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public class LoggableAttribute : Attribute
{
public string Category { get; }
public int Priority { get; set; } = 1;</p><pre class='brush:php;toolbar:false;'>public LoggableAttribute(string category)
{
    Category = category ?? throw new ArgumentNullException(nameof(category));
}

}

如何在代码中应用并读取自定义 Attribute

应用很简单:[Loggable("API")] 直接写在类或方法上。读取则依赖反射——注意:默认不会自动加载程序集中的所有类型,你得明确拿到 Type 或 MethodInfo 对象再查。

容易踩的坑是调用 GetCustomAttribute<t>()</t> 时没处理 null 返回,或误用 GetCustomAttributes(true) 在非继承场景下多传了参数。

Umbrel Proxy Manager
Umbrel Proxy Manager

高效管理与交互 Umbrel 代理服务,支持 Docker 容器。自动发现运行服务,将内部 Docker IP 映射为可访问的外部地址。

下载
  • 用 typeof(MyClass).GetCustomAttribute<loggableattribute>()</loggableattribute> 获取单个实例(返回 null 表示没找到)
  • 用 methodInfo.GetCustomAttributes<loggableattribute>()</loggableattribute> 获取所有匹配实例(返回 LoggableAttribute[])
  • 第 2 个参数 inherit(如 GetCustomAttribute<t>(true)</t>)只在基类/接口上有该特性且当前类型未重写时才生效,多数业务场景保持默认 false 更可控
  • 若特性有 AllowMultiple = true,别用单值获取方法,否则只拿到第一个
var attr = typeof(Program).GetCustomAttribute<LoggableAttribute>();
if (attr != null)
{
    Console.WriteLine($"{attr.Category}, Priority: {attr.Priority}");
}
<p>// 方法上的多个实例
var method = typeof(Program).GetMethod("DoWork");
var attrs = method?.GetCustomAttributes<LoggableAttribute>().ToArray();
foreach (var a in attrs)
{
Console.WriteLine($"Method attr: {a.Category}");
}

为什么 GetCustomAttribute 返回 null?常见排查点

不是代码写错了,而是反射读取失败的几个高频原因:特性类没加 public、目标元素没实际应用该特性、反射查询路径不对(比如查了父类却忘了 Inherited = true),或者用了不匹配的泛型类型参数。

  • 确认特性类本身是 public class XxxAttribute : Attribute,不能是 internal 或嵌套在其他类里(除非宿主类也是 public)
  • 确认你查的是正确对象:类特性查 typeof(X).GetCustomAttribute(),方法特性查 typeof(X).GetMethod("Y").GetCustomAttribute()
  • 如果特性定义了 Inherited = false,子类即使没重复标注也不会从基类继承过来
  • 确保程序集已加载——动态编译或插件场景下,Assembly.LoadFrom() 后再查,别查当前 Assembly.GetExecutingAssembly() 以外的类型却不加载
  • 泛型参数必须完全一致:比如定义的是 MyAttr<string></string>,就不能用 GetCustomAttribute<myattr>()</myattr> 去拿

能否在编译期做检查或生成代码?

原生 Attribute 本身不触发编译期逻辑。C# 9+ 的 Source Generator 可以扫描特性并生成新文件,但那是另一层机制——特性只是标记,Generator 才是干活的。别指望加个 [Obsolete] 风格的编译警告靠自定义特性自动实现。

真正需要编译期干预时,得配合 Analyzer + Source Generator,且用户项目要引用对应 NuGet 包。单纯靠 Attribute 类做不到强制校验或报错。

  • 运行时逻辑(如 AOP 日志)用反射读取即可,简单直接
  • 想拦截方法调用?得结合 DynamicProxy、Castle.Core 或 .NET 6+ 的 DispatchProxy,不是靠特性本身
  • 想让 IDE 显示警告?必须写 Roslyn Analyzer,单独的 Attribute 类毫无作用
  • 注意性能:频繁反射查特性建议缓存结果(如用 ConcurrentDictionary<memberinfo t></memberinfo>),尤其在 hot path 上

特性本身只是元数据容器,它的价值完全取决于你怎么读、何时读、读完怎么用。别把它当成魔法开关,也别低估反射调用的开销。

热门AI工具

更多
花生AI
花生AI Hot

一款AI视频创作工具,主要用于B站推出的AI视频创作工具,适合需要提升相关任务效率的用户。

讯飞智文

一款面向学习和办公场景的AI文档创作工具,可辅助生成PPT与Word文档,提高资料整理和内容制作效率。

墨刀AI
墨刀AI Hot

一款AI图像与设计工具,主要用于产品经理的专属智能体,适合需要提升相关任务效率的用户。

码上飞
码上飞 Hot

一款基于自然语言的AI应用开发平台,可根据用户描述自动生成应用程序,帮助个人和团队快速完成应用原型与开发。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

PixPix
PixPix Hot

PixPix是一款面向电商视觉生产的AI商品图生成工具。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

切问学术

切问学术是一款AI论文写作工具,复旦大学NLP团队推出的AI学术智能体。

相关专题

更多
string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

7199

2023.08.02

c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

549

2023.09.22

java中null的用法
java中null的用法

在Java中,null表示一个引用类型的变量不指向任何对象。可以将null赋值给任何引用类型的变量,包括类、接口、数组、字符串等。想了解更多null的相关内容,可以阅读本专题下面的文章。

1698

2024.03.01

c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

549

2023.09.22

java中null的用法
java中null的用法

在Java中,null表示一个引用类型的变量不指向任何对象。可以将null赋值给任何引用类型的变量,包括类、接口、数组、字符串等。想了解更多null的相关内容,可以阅读本专题下面的文章。

1698

2024.03.01

c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

549

2023.09.22

java中null的用法
java中null的用法

在Java中,null表示一个引用类型的变量不指向任何对象。可以将null赋值给任何引用类型的变量,包括类、接口、数组、字符串等。想了解更多null的相关内容,可以阅读本专题下面的文章。

1698

2024.03.01

硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

3228

2023.10.19

DBeaver怎么导入数据
DBeaver怎么导入数据

DBeaver数据导入专题,整理Excel、CSV、SQL、TXT等常见文件的数据导入方法,涵盖目标数据库选择、数据表创建、字段映射、数据类型设置及导入参数配置等操作,帮助用户将本地文件中的数据写入数据库,并了解批量导入、重复数据处理和导入失败等常见问题的解决方法。

20

2026.11.05

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn