当前位置: 首页 > news >正文

跟着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 编译器扩展的基本原理。
http://www.lqws.cn/news/463933.html

相关文章:

  • Pytorch3D 中涉及的知识点汇总
  • 【Flutter】状态管理框架Provider和Get对比分析(面试常用)
  • Python内存使用分析工具深度解析与实践指南(下篇)
  • 江科大STM32入门:DMA传输数据
  • java 找出两个json文件的不同之处
  • 神经网络中的均方误差(Mean Squared Error)详解
  • 自定义OceanBase集群安装并使用OCP接管集群
  • 【DCS开源项目】—— Lua 如何调用 DLL、DLL 与 DCS World 的交互
  • LeetCode第279题_完全平方数
  • Vue3 的生命周期:从 Composition API 视角看
  • DeepEP开源MoE模型分布式通信库
  • Linux运维新人自用笔记(Ubuntu磁盘命名规则、新磁盘分区、主流文件系统类型、mkfs命令格式化文件系统、临时和永久挂载、挂载报错、dd指令)
  • 2.7 Python方法调用机制解析:从描述符到字节码执行
  • 5.2 Qt Creator 使用FFmpeg库
  • win环境使用openssl创建p12证书
  • 微前端MFE:(React 与 Angular)框架之间的通信方式
  • word-spacing 属性
  • Kubernetes控制平面组件:Kubelet详解(八):容器存储接口 CSI
  • C++链表的虚拟头节点
  • 课程目录:腾讯混元3D × Unity3D全流程开发
  • Python pytesseract【OCR引擎库】 简介
  • 【JVM|内存结构】第一天
  • 【论文笔记】【强化微调】TinyLLaVA-Video-R1:小参数模型也能视频推理
  • Spring-MyBatis基本操作
  • linux weston flutter remote desktop
  • 2025年- H83-Lc191--139.单词拆分(动态规划)--Java版
  • JF - 600MT称重变送器与Modbus TCP转Profibus DP网关通讯案例
  • MCPServer编程与CLINE配置调用MCP
  • 项目练习:Jaspersoft Studio制作PDF报表时,detail和column footer之间存在很大的空白区
  • SkyWalking探针技术监控Spring Boot微服务——部署与应用详解