自定义特性必须继承attribute类并推荐用[attributeusage]指定使用范围,构造函数参数和公开属性类型须为编译时常量,getcustomattribute返回null常见于未设inherit:true或跨程序集未正确引用特性类型。

怎么定义一个能被读取的自定义 Attribute
自定义特性必须显式继承 Attribute 类,且推荐加 [AttributeUsage] 控制使用范围。不加这个修饰符,编译器不会报错,但运行时可能无法通过反射获取到——尤其在 .NET Core / .NET 5+ 默认只加载“可访问”的特性。
-
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = false, AllowMultiple = true)]:明确允许用在类和方法上,不继承,允许多个实例 - 构造函数参数必须是编译时常量类型(
string、int、typeof(...)、枚举等),不能是List<t></t>或匿名对象 - 公开属性必须有
get,且类型也要是常量兼容类型;设set没用,反射读取时不会触发
示例:
[AttributeUsage(AttributeTargets.Method)]
public class ApiVersionAttribute : Attribute
{
public ApiVersionAttribute(string version) => Version = version;
public string Version { get; }
}
为什么 GetCustomAttribute<t>()</t> 返回 null
最常见原因是没传 inherit: true,或者目标成员根本没应用该特性。另一个隐蔽问题是:泛型重载版本默认不查找基类上的特性(即使 Inherited = true 在定义时设了),必须显式指定 inherit: true 才会沿继承链向上查。
- 对
MethodInfo、PropertyInfo等调用时,GetCustomAttribute<t>()</t>默认inherit: false - 如果特性定义时写了
Inherited = false,那即使你传inherit: true也没用,它会被强制忽略 - 接口实现的方法上,特性不会自动从接口继承下来;得在具体方法上显式标注
正确写法:
var attr = method.GetCustomAttribute<apiversionattribute>(inherit: true);</apiversionattribute>
GetCustomAttributes() 和 GetCustomAttribute<t>()</t> 性能差多少
差别很小,但语义完全不同:GetCustomAttribute<t>()</t> 找到第一个就停,GetCustomAttributes() 总是返回所有匹配项(包括重复的)。如果你只关心“有没有”,用前者更直白;如果要遍历多个同类型特性,后者更合适。
- 两者底层都走同一套反射缓存逻辑,.NET 6+ 对常用类型做了 JIT 缓存优化
- 不要在热路径(比如每请求都调用)里反复调用它们;建议启动时扫描一次,缓存结果(如
Dictionary<methodinfo apiversionattribute></methodinfo>) - 注意:带
inherit: true的调用开销略高,因为要递归检查基类和接口
跨程序集时读不到自定义特性怎么办
典型现象是调试时能看到特性存在,但 GetCustomAttribute 始终返回 null。根本原因通常是目标程序集没被 JIT 加载,或特性类型所在的程序集未被引用(尤其是 NuGet 包中定义的特性)。
- 确保特性类型所在的 DLL 已被当前 AppDomain 加载(.NET Core 中是
AssemblyLoadContext) - 如果特性定义在 A.dll,你在 B.dll 中使用它,那么 B.dll 必须直接或间接引用 A.dll;否则反射能拿到
CustomAttributeData,但无法构造出强类型实例 - 用
GetCustomAttributeData()可绕过类型加载问题,但它返回的是原始元数据,你需要手动解析构造函数参数和命名参数
安全做法是:把特性定义放在共享基础库,并让所有用到它的项目都直接引用它。










