文章目录

C# 特性:Attribute 与反射获取

发布于 2026-04-07 08:17:43 · 浏览 9 次 · 评论 0 条

C# 特性:Attribute 与反射获取


特性(Attribute)本质上是附加在代码元素(类、方法、属性等)上的元数据标签。它不改变程序本身的运行逻辑,但能提供额外的描述信息。通过反射(Reflection),你可以在程序运行期间动态读取这些标签的内容。按照以下步骤,完成从创建到读取的完整流程。


阶段一:定义特性类

特性本身就是一个普通的 C# 类,但必须遵守特定规则。

  1. 创建一个继承自 System.Attribute 的公共类。在类名末尾添加 Attribute 后缀,这是 C# 的命名惯例,使用特性时该后缀会被自动省略。

    public class VersionInfoAttribute : System.Attribute
    {
    }
  2. 配置作用范围。使用 AttributeUsage 标记该特性,指定它可以贴在哪些元素上(如类、方法、字段),并设置是否允许重复标记。

    [System.AttributeUsage(System.AttributeTargets.Class | System.AttributeTargets.Method, AllowMultiple = true)]
    public class VersionInfoAttribute : System.Attribute
    {
    }
  3. 声明数据存储字段。特性需要载体来保存信息,添加只读属性或公共属性。构造函数负责接收必传参数。

    public class VersionInfoAttribute : System.Attribute
    {
     public VersionInfoAttribute(string author)
     {
         Author = author;
         CreatedDate = System.DateTime.Now;
     }
    
     public string Author { get; }
     public string Version { get; set; } = "1.0.0";
     public System.DateTime CreatedDate { get; }
    }

阶段二:将特性附加到目标代码

定义完成后,即可像贴标签一样将其绑定到类或方法上。

  1. 定位需要标记的代码位置。特性必须放置在目标元素正上方,用方括号 [] 包裹。
  2. 传入参数。调用特性类的构造函数,必传参数直接填写,可选参数使用 属性名 = 值 的格式。
    [VersionInfo("张三", Version = "2.5.0")]
    public class DataProcessor
    {
     [VersionInfo("李四", Version = "1.2.0")]
     public void Execute()
     {
         // 核心业务逻辑
     }
    }

阶段三:使用反射机制读取特性

程序运行时,通过 System.Reflection 命名空间提取标签信息。

  1. 获取目标类型的元数据。调用 typeof() 或实例的 .GetType() 方法获取 Type 对象。
  2. 提取特性对象。使用 GetCustomAttributesGetCustomAttribute<T> 泛型方法。推荐使用泛型版本,它直接返回强类型对象,避免手动类型转换。
  3. 遍历结果集合。如果特性允许重复,方法会返回数组或可枚举集合。

为清晰对比不同场景的提取方式,参考下表配置你的反射调用:

目标位置 核心反射方法 返回类型说明 调用示例片段
类级别 Type.GetCustomAttribute<T>() 返回单个特性实例 typeof(DataProcessor).GetCustomAttribute<VersionInfoAttribute>()
类级别(允许多个) Type.GetCustomAttributes<T>() 返回 IEnumerable<T> typeof(DataProcessor).GetCustomAttributes<VersionInfoAttribute>()
方法级别 MethodInfo.GetCustomAttribute<T>() 返回单个特性实例 method.GetCustomAttribute<VersionInfoAttribute>()
属性/字段级别 PropertyInfo.GetCustomAttribute<T>() 返回单个特性实例 prop.GetCustomAttribute<VersionInfoAttribute>()

阶段四:完整读取与数据解析代码

将上述步骤整合为可执行的控制台程序。严格按照顺序编写代码即可直接运行。

  1. 导入反射命名空间。在文件头部引入 using System.Reflection;
  2. 编写主读取逻辑。获取目标类型后,使用 foreach 循环处理可能存在的多个特性实例,并安全读取属性值。
    
    using System;
    using System.Reflection;

class Program
{
static void Main()
{
// 1. 获取目标类型的 Type 对象
Type targetType = typeof(DataProcessor);

    // 2. 提取该类型上的所有特性
    var attributes = targetType.GetCustomAttributes<VersionInfoAttribute>();

    // 3. 遍历并读取特性数据
    foreach (var attr in attributes)
    {
        Console.WriteLine($"作者: {attr.Author}");
            Console.WriteLine($"版本: {attr.Version}");
        Console.WriteLine($"创建时间: {attr.CreatedDate:yyyy-MM-dd}");
        }

        // 4. 演示方法级别的特性获取
        MethodInfo method = targetType.GetMethod("Execute");
        if (method != null)
        {
            var methodAttr = method.GetCustomAttribute<VersionInfoAttribute>();
            if (methodAttr != null)
            {
                Console.WriteLine($"方法开发者: {methodAttr.Author}");
        }
    }
}

}


3. **处理**潜在的空引用异常。反射获取特性时,若目标未打标签,返回结果为 `null`(单实例方法)或空集合(多实例方法)。**添加**空值检查逻辑,避免程序崩溃。上述代码中已使用 `if (methodAttr != null)` 进行防护。

---

## 关键避坑指南
特性与反射的组合在实际开发中极易因细节疏忽导致读取失败。严格执行以下检查项。

1. **核对** `AttributeUsage` 的 `AttributeTargets` 枚举值。如果只允许贴在类上,却尝试贴在接口上,编译器会直接报错。
2. **确认**泛型参数类型。调用 `GetCustomAttribute<T>()` 时,尖括号 `<>` 内的类型**必须**与你定义的特性类名完全一致(不包含 `Attribute` 后缀亦可,但建议带全称以避免歧义)。
3. **注意**编译优化影响。在 `Release` 模式下启用“优化代码”时,未被引用的方法或类型可能被裁剪。若反射读取失效,**检查**项目发布配置中的“裁剪未引用程序集”或“优化”开关。
4. **区分**实例方法与静态方法的反射获取。静态方法同样通过 `GetMethod` 获取,但若方法名重载,需额外传入 `BindingFlags.Static | BindingFlags.Public` 参数数组以确保精准定位。

完成上述配置后,特性即可作为代码的标准化配置源,替代部分硬编码参数或外部配置文件,实现配置与逻辑的紧密绑定。

评论 (0)

暂无评论,快来抢沙发吧!

扫一扫,手机查看

扫描上方二维码,在手机上查看本文