自定义 Attribute 必须满足四个条件才能被反射读取:public 非泛型非抽象、显式继承 Attribute、用 AttributeUsage 明确目标和特性、构造函数仅含编译期常量参数;否则 GetCustomAttribute() 返回 null。

自定义 Attribute 能用,但不加 [AttributeUsage] 就大概率读不到——不是语法错,是反射查无此物。
为什么 GetCustomAttribute<t>()</t> 总返回 null
最常见原因是:没用 [AttributeUsage] 显式声明允许的目标位置,或者写错了 AttributeTargets。
-
[AttributeUsage(AttributeTargets.Method)]表示只允许标在方法上;如果误贴到类或参数上,GetMethod(...).GetCustomAttribute<mylogattribute>()</mylogattribute>一定为null - 默认
[AttributeUsage(AttributeTargets.Class)],哪怕你继承了Attribute,也不代表它能出现在方法上 - 目标方法本身非
public,且没传BindingFlags.NonPublic,GetMethod返回null,后续调用直接抛NullReferenceException - 构造函数参数用了运行时对象(如
new List<string>()</string>),编译报错,根本贴不上
Attribute 类怎么写才安全可读取
必须满足四个硬性条件,缺一不可:
- 类必须是
public、非泛型、非抽象 - 必须显式继承
System.Attribute - 必须用
[AttributeUsage]标明validOn(比如AttributeTargets.Method | AttributeTargets.Parameter)、AllowMultiple和Inherited - 构造函数参数只能是编译期常量(
string、int、typeof(...)、枚举值);公开属性必须带public get,字段不推荐
错误示范:public class LogAttribute : Attribute { public LogAttribute(List<string> tags) { ... } }</string> —— 编译失败。
反射读取时的性能和空值陷阱
GetCustomAttribute<t>()</t> 不抛异常,只返回 null,这是设计使然,但容易引发后续 NRE。
- 永远先判空:
var attr = method.GetCustomAttribute<retryattribute>(); if (attr?.MaxRetries > 0) { ... }</retryattribute> - 高频调用场景(如中间件、拦截器)别每次反射查,用
ConcurrentDictionary<methodinfo retryattribute></methodinfo>缓存结果 - 要读多个同名特性,用
method.GetCustomAttributes(typeof(RetryAttribute), false),返回object[],再逐个as RetryAttribute -
inherit = true会沿继承链向上找,若父类/接口有同名Attribute,可能拿到意料之外的实例;多数 AOP 场景建议设false
命名参数 vs 位置参数怎么传才对
构造函数参数是位置参数,public 属性是命名参数,二者在标记时写法不同:
-
[MyAttr("info")]→ 调用MyAttr(string info)构造函数 -
[MyAttr("info", Level = 3)]→ 构造函数接收"info",再赋值Level属性 - 属性名大小写敏感,
level = 3(小写 l)会编译失败 - 命名参数不能放在位置参数前面:
[MyAttr(Level = 3, "info")]是非法语法
真正容易被忽略的是:所有逻辑都在反射获取时才执行。如果构造函数里写了日志、IO 或数据库连接,每次 GetCustomAttribute 都会触发——而你可能根本没意识到它被调了多少次。


















