在 C# 中,反射(Reflection)和特性(Attributes)是两个强大的功能,它们在运行时提供元编程能力,广泛用于框架开发、对象映射和动态行为扩展。以下是对它们的详细介绍,包括定义、用法、示例和应用场景。
一、反射(Reflection)
什么是反射?
反射是 C# 运行时的一种机制,允许程序在运行时动态检查和操作类型、对象及其元数据(如类、方法、属性等)。通过反射,开发者可以:
- 获取类型信息(如类名、方法名)。
 - 动态创建对象。
 - 调用方法或访问属性/字段。
 - 检查或修改私有成员(需注意权限)。
 
反射的核心类库位于 System.Reflection 命名空间。
反射的核心类和方法
-  
Type:- 表示类型的元数据,是反射的核心。
 - 获取方式: 
typeof(ClassName):静态获取类型。object.GetType():从实例获取类型。
 
 -  
Assembly:- 表示程序集,可以加载和检查 DLL 或 EXE。
 
 -  
MethodInfo、PropertyInfo、FieldInfo:- 分别表示方法、属性和字段的元数据。
 
 -  
Activator:- 用于动态创建对象实例。
 
 
示例 1:基本反射操作
using System;
using System.Reflection;class Person
{public string Name { get; set; }private int age = 25;public void SayHello(){Console.WriteLine($"Hello, I'm {Name}, {age} years old.");}
}class Program
{static void Main(){// 获取类型Type type = typeof(Person);Console.WriteLine($"类名: {type.Name}");// 创建实例object instance = Activator.CreateInstance(type);// 设置属性PropertyInfo nameProp = type.GetProperty("Name");nameProp.SetValue(instance, "Alice");// 获取私有字段并修改FieldInfo ageField = type.GetField("age", BindingFlags.NonPublic | BindingFlags.Instance);ageField.SetValue(instance, 30);// 调用方法MethodInfo method = type.GetMethod("SayHello");method.Invoke(instance, null);// 输出所有公共方法Console.WriteLine("\n公共方法:");foreach (MethodInfo m in type.GetMethods(BindingFlags.Public | BindingFlags.Instance)){Console.WriteLine(m.Name);}}
}
 
输出
类名: Person
Hello, I'm Alice, 30 years old.公共方法:
get_Name
set_Name
SayHello
ToString
Equals
GetHashCode
GetType
 
说明
typeof:获取Person的类型信息。Activator.CreateInstance:动态创建实例。GetProperty和SetValue:访问和修改属性。GetField:通过BindingFlags获取私有字段。Invoke:动态调用方法。
示例 2:加载程序集
using System;
using System.Reflection;class Program
{static void Main(){// 加载当前程序集Assembly assembly = Assembly.GetExecutingAssembly();foreach (Type type in assembly.GetTypes()){Console.WriteLine($"类型: {type.FullName}");}}
}
 
输出
类型: Program
 
说明
Assembly.GetExecutingAssembly:获取当前程序集。GetTypes:列出程序集中所有类型。
反射的优缺点
- 优点: 
- 动态性:运行时决定行为,适合插件系统或框架。
 - 灵活性:无需提前知道类型即可操作。
 
 - 缺点: 
- 性能开销:反射比直接调用慢。
 - 安全性:可能暴露私有成员,需谨慎使用。
 
 
二、特性(Attributes)
什么是特性?
特性是 C# 中的一种声明性标签,用于为代码元素(如类、方法、属性等)附加元数据。特性在运行时可以通过反射读取,用于控制行为或提供额外信息。
特性定义在 System 命名空间中,常用基类是 Attribute。
特性的定义与使用
-  
定义特性:
- 继承自 
Attribute,添加[AttributeUsage]指定适用范围。 
 - 继承自 
 -  
应用特性:
- 使用方括号 
[ ]标记在代码元素上。 
 - 使用方括号 
 -  
读取特性:
- 通过反射的 
GetCustomAttributes方法获取。 
 - 通过反射的 
 
示例 1:自定义特性
using System;
using System.Reflection;// 定义特性
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
public class DescriptionAttribute : Attribute
{public string Description { get; }public DescriptionAttribute(string description){Description = description;}
}// 使用特性
[Description("这是一个测试类")]
class TestClass
{[Description("这是一个测试方法")]public void TestMethod(){Console.WriteLine("Hello from TestMethod!");}
}class Program
{static void Main(){// 获取类特性Type type = typeof(TestClass);DescriptionAttribute classAttr = (DescriptionAttribute)Attribute.GetCustomAttribute(type, typeof(DescriptionAttribute));Console.WriteLine($"类描述: {classAttr?.Description}");// 获取方法特性MethodInfo method = type.GetMethod("TestMethod");DescriptionAttribute methodAttr = (DescriptionAttribute)method.GetCustomAttribute(typeof(DescriptionAttribute));Console.WriteLine($"方法描述: {methodAttr?.Description}");// 调用方法object instance = Activator.CreateInstance(type);method.Invoke(instance, null);}
}
 
输出
类描述: 这是一个测试类
方法描述: 这是一个测试方法
Hello from TestMethod!
 
说明
[AttributeUsage]:限制特性只能用于类和方法,且不可重复。GetCustomAttribute:获取指定类型的特性实例。Description:特性中存储的元数据。
示例 2:内置特性 - [Obsolete]
 
using System;class Program
{[Obsolete("此方法已过时,请使用 NewMethod", false)] // false 表示警告,true 表示错误static void OldMethod(){Console.WriteLine("Old Method");}static void NewMethod(){Console.WriteLine("New Method");}static void Main(){OldMethod(); // 编译器会发出警告NewMethod();}
}
 
输出(带警告)
Old Method
New Method
 
说明
[Obsolete]:标记方法为过时,编译时提示开发者。
特性的应用场景
-  
框架开发:
- ASP.NET Core 使用 
[Route]、[HttpGet]等特性定义路由和行为。 - Entity Framework 使用 
[Table]、[Key]配置数据库映射。 
 - ASP.NET Core 使用 
 -  
验证与描述:
[Required]、[MaxLength]用于数据验证。[Description]添加文档信息。
 -  
条件编译:
[Conditional("DEBUG")]在特定条件下执行方法。
 
反射与特性的结合
反射和特性经常一起使用,例如:
- 依赖注入:通过反射扫描带有特定特性的类,动态注入。
 - 序列化:检查 
[Serializable]或自定义特性,决定序列化字段。 
优缺点
- 优点: 
- 声明式编程:减少硬编码,提高可维护性。
 - 元数据丰富:为工具和框架提供信息。
 
 - 缺点: 
- 运行时开销:读取特性需要反射。
 - 复杂度:过度使用可能使代码难以理解。
 
 
总结
- 反射:运行时动态操作类型和对象,适合需要灵活性的场景(如插件系统)。
 - 特性:为代码添加元数据,配合反射实现声明式逻辑(如框架配置)。
 
通过反射,你可以动态调用方法或创建实例;通过特性,你可以为代码附加规则或描述。这两者在 C# 中是构建高级功能(如 ORM、AOP)的基石。