跟着AI学习C# Day27
📅 Day 27:C# 中的反射(Reflection)与元编程
✅ 学习目标:
- 理解什么是 反射(Reflection);
- 掌握如何在运行时动态获取类型信息、创建实例、调用方法;
- 使用反射实现通用逻辑(如对象克隆、属性赋值等);
- 理解反射在框架开发中的典型应用(依赖注入、序列化、ORM);
- 了解
System.Reflection.Emit
实现动态代码生成; - 掌握性能优化技巧(缓存反射结果、使用表达式树代替 Invoke);
- 编写一个基于反射的通用对象克隆器。
🧠 一、什么是反射?
反射(Reflection) 是 .NET 提供的一种机制,允许你在运行时查看程序集(Assembly)中的类型信息,并动态创建对象、访问成员、调用方法等。
主要用途:
场景 | 示例 |
---|---|
动态加载程序集 | 插件系统、模块化架构 |
获取类型信息 | 查看类、接口、方法、属性等 |
创建实例和调用方法 | 工厂模式、依赖注入容器 |
属性操作 | 数据绑定、ORM 映射 |
自定义特性解析 | 验证模型、权限控制 |
表达式树构建 | 构建 LINQ 查询、动态条件过滤 |
🔍 二、基本反射操作
1️⃣ 获取类型信息
Type type = typeof(string);
Console.WriteLine("类型名称:" + type.FullName);
或通过对象获取:
Person person = new Person();
Type type = person.GetType();
2️⃣ 获取构造函数并创建实例
Type type = typeof(Person);
object obj = Activator.CreateInstance(type);
指定参数:
object obj = Activator.CreateInstance(type, "张三", 25);
3️⃣ 获取方法并调用
MethodInfo method = type.GetMethod("SayHello");
method.Invoke(obj, null);
带参数的方法:
MethodInfo method = type.GetMethod("SetName", new[] { typeof(string) });
method.Invoke(obj, new object[] { "李四" });
4️⃣ 获取属性并读取/设置值
PropertyInfo prop = type.GetProperty("Name");
string name = (string)prop.GetValue(obj);
prop.SetValue(obj, "王五");
5️⃣ 获取字段并操作
FieldInfo field = type.GetField("_age", BindingFlags.NonPublic | BindingFlags.Instance);
field.SetValue(obj, 30);
int age = (int)field.GetValue(obj);
💡 三、反射的应用场景
应用 | 反射的作用 |
---|---|
ORM 框架(如 EF Core) | 将数据库记录映射为实体对象 |
JSON 序列化(如 Newtonsoft.Json) | 读取对象属性并转换为 JSON 字符串 |
依赖注入容器 | 根据配置自动解析服务和依赖 |
单元测试框架(xUnit/NUnit) | 找到所有测试方法并执行 |
MVC/Web API 框架 | 路由匹配、Action 方法调用 |
插件系统 | 动态加载 DLL 并调用其中的方法 |
🧱 四、Expression 替代反射提高性能
反射虽然强大,但性能较低。可以通过 表达式树(Expression Tree) 或 委托缓存 来优化。
示例:构建属性访问委托
public static Func<T, string> CreatePropertyGetter<T>(string propertyName)
{ParameterExpression param = Expression.Parameter(typeof(T));MemberExpression property = Expression.Property(param, propertyName);UnaryExpression cast = Expression.Convert(property, typeof(string));return Expression.Lambda<Func<T, string>>(cast, param).Compile();
}
使用:
var getter = CreatePropertyGetter<Person>("Name");
Person p = new Person { Name = "Tom" };
Console.WriteLine(getter(p)); // 输出 Tom
🧩 五、动态代码生成(System.Reflection.Emit)
你可以使用 System.Reflection.Emit
在运行时生成新的类型和方法,用于高性能场景(如 AOP、代理类生成、动态编译)。
示例:动态创建一个类并调用方法
AssemblyName assemblyName = new AssemblyName("DynamicAssembly");
AssemblyBuilder assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");TypeBuilder typeBuilder = moduleBuilder.DefineType("MyDynamicClass", TypeAttributes.Public);
MethodBuilder methodBuilder = typeBuilder.DefineMethod("SayHello",MethodAttributes.Public | MethodAttributes.Static,typeof(void),Type.EmptyTypes);ILGenerator il = methodBuilder.GetILGenerator();
il.EmitWriteLine("Hello from dynamic class!");
il.Emit(OpCodes.Ret);Type dynamicType = typeBuilder.CreateType();
MethodInfo method = dynamicType.GetMethod("SayHello");
method.Invoke(null, null); // 输出:Hello from dynamic class!
💪 六、实战练习:通用对象克隆器
功能要求:
- 支持任意类型的对象深拷贝;
- 使用反射复制所有公共属性;
- 忽略只读属性;
- 返回新实例。
示例代码:
public static T Clone<T>(T source) where T : class, new()
{if (source == null) return null;T target = new T();foreach (PropertyInfo prop in typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance)){if (!prop.CanRead || !prop.CanWrite) continue;object value = prop.GetValue(source);prop.SetValue(target, value);}return target;
}
使用示例:
Person original = new Person { Name = "Alice", Age = 30 };
Person copy = Clone(original);
⚡ 七、性能优化建议
技术 | 说明 |
---|---|
缓存 MethodInfo / PropertyInfo | 避免重复反射查询 |
使用委托替代 Invoke | 性能提升可达几十倍 |
使用 Expression Tree 构建委托 | 更安全、更高效 |
使用 System.Runtime.CompilerServices.Unsafe | 高性能场景下直接操作内存 |
使用 Source Generator(C# 9+) | 在编译期生成代码,避免运行时反射 |
📝 小结
今天你学会了:
- 什么是反射及其作用;
- 如何在运行时获取类型信息、创建实例、调用方法;
- 掌握了反射在框架开发中的典型应用场景;
- 学会了使用表达式树优化反射性能;
- 了解了
System.Reflection.Emit
实现动态代码生成; - 编写了一个基于反射的通用对象克隆器;
- 掌握了多种反射性能优化技巧。
反射是 C# 强大而灵活的特性之一,尤其适用于构建插件系统、ORM、序列化库、AOP 等高级框架。
🧩 下一步学习方向(Day 28)
明天我们将进入一个新的主题 —— C# 中的源生成器(Source Generators)与编译时元编程,你将学会:
- 什么是源生成器(Source Generator);
- 如何在编译阶段生成 C# 代码;
- 使用源生成器替代运行时反射;
- 构建高性能、零运行时开销的实用工具;
- 实现一个基于源生成器的自动化 DTO 映射器;
- 掌握 Roslyn 编译器扩展的基本原理。