自定义Attribute需继承System.Attribute并用AttributeUsage限定目标(如仅方法),类名建议以Attribute结尾;应用时通过反射读取,缺失时返回null而非异常,性能敏感场景应缓存或配合Source Generator在编译期处理。
自定义 Attribute 必须继承 System.Attribute,且推荐显式指定 AttributeUsage 来限定它能修饰的目标(比如只允许用在方法上)。不加这个约束,编译器不会报错,但运行时反射读取时可能拿到意外位置的实例。
常见错误是忘记加 [AttributeUsage(AttributeTargets.Method)],结果特性被误标在类或属性上,后续通过 GetCustomAttribute 查不到——因为根本没允许它出现在方法上。
Attribute 结尾(如 LogExecutionTimeAttribute),C# 允许使用时省略后缀([LogExecutionTime])public 属性决定「命名参数」Attribute 不行,得用源生成器或 ModuleInitializer 配合应用很简单:[MyCustomAttr("value", Level = 2)];读取必须靠反射,且注意:只有在目标成员被反射访问时,.NET 才会解析并实例化该特性——不是声明时就创建对象。
典型场景是 AOP 式日志、权限校验、序列化控制。但别在热路径里反复调用 GetCustomAttribute,它有反射开销;建议启动时缓存(比如用 ConcurrentDictionary)。
typeof(MyClass).GetMethod("MyMethod").GetCustomAttribute() 获取单个实例GetCustomAttributes(true) 的第二个参数为 true 表示查找继承链上的特性GetCustomAttribute 设计为“查无此特则返回 null”,这是有意为之:绝大多数使用场景下,缺失特性是合法状态(比如某个方法不需要日志,就不打标签),没必要用异常干扰流程。
容易踩的坑是直接对返回值调用方法而不判空,导致 NullReferenceException。更隐蔽的问题是:你写了 [MyAttr],但忘了在类/方法上加 public 访问修饰符,导致反射拿不到该成员——此时 GetMethod 返回 null,再调 GetCustomAttribute 就是空引用。
public,非 public 成员需用 BindingFlags.NonPublic | BindingFlags.Instance
TrimMode=link,需在 .csproj 中添加 false )typeof(MyClass).GetMethods().Select(m => (m.Name, m.GetCustomAttribute())) 快速验证是否被正确识别纯 Attribute 只能在运行时通过反射读取,而 Source Generator 能在编译期扫描这些特性,生成额外代码(比如自动注册 Han

比如你写了个 [Route("/api/users")],不用等请求进来再反射找路由,Generator 可直接生成 MapGet 调用。但要注意:Generator 看不到私有成员,也无法处理动态构造的类型名(如字符串拼接的类名)。
InternalsVisibleTo 隐藏的public,且所有构造函数和属性也要是 public,否则 Generator 读不到值实际项目里最常出问题的,是特性被定义成 internal 却期望在另一个程序集里被反射读到,或者忘了给 AttributeUsage 加 Inherited = true 导致子类方法拿不到父类标记的特性。