一、概念对比
特性(Attributes)
是什么:给代码元素贴的"标签"或"注解"
作用:为代码添加额外信息(元数据)
时机:编译时和运行时都可以读取
反射(Reflection)
是什么:在运行时"查看"和"操作"代码的能力
作用:动态获取类型信息、创建对象、调用方法
时机:只能在运行时使用
二、关系:特性 + 反射 = 强大功能
工作流程:
text
1. 用特性给代码"贴标签"(编译时) 2. 用反射读取这些"标签"(运行时) 3. 根据标签信息执行相应操作
三、基本使用格式
1. 定义自定义特性
csharp
// 1. 继承Attribute类 // 2. 使用AttributeUsage指定应用范围 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] public class CustomAttribute : Attribute { // 属性 public string Description { get; set; } public int Version { get; set; } // 构造方法 public CustomAttribute(string description) { Description = description; Version = 1; } }2. 使用特性
csharp
[Custom("学生类", Version = 2)] public class Student { [Custom("姓名属性")] public string Name { get; set; } [Custom("学习方法")] public void Study() { } }3. 用反射读取特性
csharp
// 获取类型信息 Type type = typeof(Student); // 读取类上的特性 CustomAttribute classAttr = (CustomAttribute)type.GetCustomAttribute(typeof(CustomAttribute)); if (classAttr != null) { Console.WriteLine($"类描述: {classAttr.Description}"); Console.WriteLine($"版本: {classAttr.Version}"); } // 读取属性上的特性 foreach (var property in type.GetProperties()) { var attr = property.GetCustomAttribute<CustomAttribute>(); if (attr != null) { Console.WriteLine($"属性 {property.Name}: {attr.Description}"); } } // 读取方法上的特性 foreach (var method in type.GetMethods()) { var attr = method.GetCustomAttribute<CustomAttribute>(); if (attr != null) { Console.WriteLine($"方法 {method.Name}: {attr.Description}"); } }四、AttributeUsage参数详解
1. AttributeTargets(可以贴在哪里)
csharp
// 单个目标 AttributeTargets.Class // 类 AttributeTargets.Method // 方法 AttributeTargets.Property // 属性 AttributeTargets.Field // 字段 AttributeTargets.Constructor // 构造方法 AttributeTargets.Struct // 结构体 AttributeTargets.Interface // 接口 AttributeTargets.Enum // 枚举 AttributeTargets.Parameter // 参数 AttributeTargets.Assembly // 程序集 // 多个目标(用 | 连接) AttributeTargets.Class | AttributeTargets.Method // 所有成员 AttributeTargets.All
2. AllowMultiple(是否可以贴多个)
csharp
AllowMultiple = false // 只能贴一个(默认) AllowMultiple = true // 可以贴多个 // 示例: [MyAttr("标签1")] [MyAttr("标签2")] // 只有当AllowMultiple=true时才允许 public class MyClass { }3. Inherited(是否被子类继承)
csharp
Inherited = true // 子类可以继承特性(默认) Inherited = false // 子类不继承特性
五、反射常用方法
1. 获取类型信息
csharp
// 三种方式获取Type对象 Type type1 = typeof(Student); // 编译时已知类型 Type type2 = obj.GetType(); // 运行时对象 Type type3 = Type.GetType("命名空间.类名"); // 通过字符串 // 常用属性和方法 type.Name // 类名 type.FullName // 完整名称(含命名空间) type.Namespace // 命名空间 type.BaseType // 父类类型 type.IsClass // 是否是类 type.IsPublic // 是否是public2. 获取成员信息
csharp
// 获取所有方法 MethodInfo[] methods = type.GetMethods(); // 获取所有属性 PropertyInfo[] properties = type.GetProperties(); // 获取所有字段 FieldInfo[] fields = type.GetFields(); // 获取所有构造方法 ConstructorInfo[] constructors = type.GetConstructors();
3. 动态创建对象和调用方法
csharp
// 1. 创建对象 object obj = Activator.CreateInstance(type); // 2. 设置属性值 PropertyInfo prop = type.GetProperty("Name"); prop.SetValue(obj, "张三"); // 3. 获取属性值 string name = (string)prop.GetValue(obj); // 4. 调用方法 MethodInfo method = type.GetMethod("Study"); method.Invoke(obj, null); // 无参数方法 // 5. 调用带参数的方法 MethodInfo method2 = type.GetMethod("SetAge"); method2.Invoke(obj, new object[] { 20 });六、完整示例:数据验证系统
1. 定义验证特性
csharp
[AttributeUsage(AttributeTargets.Property)] public class ValidationAttribute : Attribute { public string ErrorMessage { get; set; } public bool Required { get; set; } public int MinLength { get; set; } = 0; public int MaxLength { get; set; } = int.MaxValue; }2. 使用特性标记模型
csharp
public class Student { [Validation(Required = true, MinLength = 2, ErrorMessage = "姓名不能为空且至少2个字符")] public string Name { get; set; } [Validation(Required = true, ErrorMessage = "年龄必须填写")] public int Age { get; set; } [Validation(Required = true, MaxLength = 11, ErrorMessage = "手机号格式不正确")] public string Phone { get; set; } }3. 反射读取并验证
csharp
public class Validator { public static List<string> Validate(object obj) { List<string> errors = new List<string>(); Type type = obj.GetType(); foreach (PropertyInfo property in type.GetProperties()) { // 获取特性 ValidationAttribute attr = property.GetCustomAttribute<ValidationAttribute>(); if (attr != null) { // 获取属性值 object value = property.GetValue(obj); // 验证必填 if (attr.Required && (value == null || string.IsNullOrEmpty(value.ToString()))) { errors.Add(attr.ErrorMessage); continue; } // 验证长度 if (value != null) { string strValue = value.ToString(); if (strValue.Length < attr.MinLength || strValue.Length > attr.MaxLength) { errors.Add(attr.ErrorMessage); } } } } return errors; } }4. 使用验证器
csharp
Student student = new Student { Name = "张", // 长度不够 Age = 0, Phone = "123456789012" // 长度超过 }; var errors = Validator.Validate(student); foreach (var error in errors) { Console.WriteLine(error); }七、总结表格
| 方面 | 特性(Attribute) | 反射(Reflection) |
|---|---|---|
| 目的 | 添加元数据 | 读取和操作元数据 |
| 时机 | 编译时定义 | 运行时使用 |
| 使用方式 | 用 [] 声明 | 用 Type 类操作 |
| 常见用途 | 配置、验证、序列化 | 动态加载、插件系统 |
| 性能 | 几乎没有影响 | 较慢,避免频繁使用 |
八、最佳实践
1. 特性使用原则
csharp
// 好的做法 [AttributeUsage(AttributeTargets.Property)] // 明确目标 public class MyAttribute : Attribute { public string Description { get; } // 只读属性 public MyAttribute(string description) // 构造方法 { Description = description; } } // 使用 [My("这是一个重要的属性")] public string ImportantProperty { get; set; }2. 反射使用原则
csharp
// 缓存Type信息(提高性能) private static readonly Type _cachedType = typeof(MyClass); // 使用泛型减少类型转换 public T CreateInstance<T>() where T : new() { return new T(); // 比反射创建快 } // 必要时才用反射 if (needDynamic) { // 使用反射 } else { // 使用静态代码 }九、一句话总结
特性是贴标签,反射是读标签,两者结合实现动态编程。
text
工作流程: 1. 设计特性 → 定义标签格式 2. 标注代码 → 贴上标签 3. 反射读取 → 运行时查看标签 4. 执行逻辑 → 根据标签决定行为