getcustomattribute() 返回 null 不是 bug,而是未满足反射可读取的四个条件:特性类必须 public/非泛型/非抽象、显式继承 attribute、正确配置 attributeusage、构造函数参数为编译期常量。

GetCustomAttributenull 不是 bug,而是你没满足反射可读取的四个硬性条件——缺一不可,否则编译能过,运行时就“消失”。
为什么 [MyLog] 标了却读不到?
最常见原因是:[AttributeUsage] 缺失或写错目标。C# 不会默认把特性“广播”到所有位置,必须显式声明它允许贴在哪。
-
[AttributeUsage(AttributeTargets.Method)]→ 只能贴在方法上;若误贴到类、参数或属性上,GetMethod().GetCustomAttribute<mylogattribute>()</mylogattribute>必定返回null - 写了
[AttributeUsage(AttributeTargets.Class)]却去方法上用,一样查不到 - 没加
[AttributeUsage]修饰自定义类?那它在反射眼里“不存在”,GetCustomAttribute永远为null - 目标方法是
private或protected?GetMethod("xxx")默认只找public,得手动传BindingFlags.NonPublic | BindingFlags.Instance
自定义 Attribute 类怎么写才不会“编译通过但读不到”?
必须同时满足这四点,少一个,反射就认不出来:
- 类必须是
public、非泛型、非抽象(不能带<t></t>,不能是abstract class) - 必须显式继承
System.Attribute(不能只靠隐式继承) - 必须用
[AttributeUsage(...)]明确指定validOn(如Method)、AllowMultiple和Inherited - 构造函数参数只能是编译期常量:字符串字面量、数字、
typeof(...)、枚举值;不能传new List<string>()</string>或其他运行时对象
错误示范:public class LogAttribute : Attribute { public LogAttribute(List<string> tags) { ... } }</string> → 直接编译失败。
读取时的空值和性能陷阱
GetCustomAttribute<t>()</t> 设计就是不抛异常、只返 null,但后续直接调用 attr.MaxRetries 很容易触发 NullReferenceException。
- 永远先判空:
var attr = method.GetCustomAttribute<retryattribute>(); if (attr?.MaxRetries > 0) { ... }</retryattribute> - 高频路径(如 ASP.NET Core 中间件、拦截器)别每次反射查,用
ConcurrentDictionary<methodinfo retryattribute></methodinfo>缓存结果 - 要读多个同名特性,用
method.GetCustomAttributes(typeof(RetryAttribute), false),返回object[],再逐个as RetryAttribute -
Inherited = true会沿继承链向上找——父类有同名特性时可能拿到意料之外的实例;AOP 场景建议设false
命名参数 vs 位置参数怎么传才对?
构造函数参数是位置参数(必须提供),公开属性是命名参数(可选,用 Name = value 赋值)。
- 定义:
public MyLogAttribute(string level) { Level = level; } public string Category { get; set; } - 使用:
[MyLog("Error", Category = "Auth")]→"Error"进构造函数,Category走属性赋值 - 字段(
public string Category;)不推荐——反射不保证赋值顺序,且无法做验证逻辑 - 命名参数不能重名,也不能和位置参数同名,否则编译报错
真正难的不是写法,而是记住:特性不是语法糖,它是元数据 + 反射契约。漏掉 [AttributeUsage] 或用错 BindingFlags,代码看起来完全正常,却在运行时静默失效——这种问题最难 debug。











