跟着AI学习C# Day27

📅 Day 27:C# 中的反射(Reflection)与元编程

✅ 学习目标:

  • 理解什么是 反射(Reflection)
  • 掌握如何在运行时动态获取类型信息、创建实例、调用方法;
  • 使用反射实现通用逻辑(如对象克隆、属性赋值等);
  • 理解反射在框架开发中的典型应用(依赖注入、序列化、ORM);
  • 了解 System.Reflection.Emit 实现动态代码生成;
  • 掌握性能优化技巧(缓存反射结果、使用表达式树代替 Invoke);
  • 编写一个基于反射的通用对象克隆器。

🧠 一、什么是反射?

反射(Reflection) 是 .NET 提供的一种机制,允许你在运行时查看程序集(Assembly)中的类型信息,并动态创建对象、访问成员、调用方法等。

主要用途:

场景 示例
动态加载程序集 插件系统、模块化架构
获取类型信息 查看类、接口、方法、属性等
创建实例和调用方法 工厂模式、依赖注入容器
属性操作 数据绑定、ORM 映射
自定义特性解析 验证模型、权限控制
表达式树构建 构建 LINQ 查询、动态条件过滤

🔍 二、基本反射操作

1️⃣ 获取类型信息

csharp 复制代码
Type type = typeof(string);
Console.WriteLine("类型名称:" + type.FullName);

或通过对象获取:

csharp 复制代码
Person person = new Person();
Type type = person.GetType();

2️⃣ 获取构造函数并创建实例

csharp 复制代码
Type type = typeof(Person);
object obj = Activator.CreateInstance(type);

指定参数:

csharp 复制代码
object obj = Activator.CreateInstance(type, "张三", 25);

3️⃣ 获取方法并调用

csharp 复制代码
MethodInfo method = type.GetMethod("SayHello");
method.Invoke(obj, null);

带参数的方法:

csharp 复制代码
MethodInfo method = type.GetMethod("SetName", new[] { typeof(string) });
method.Invoke(obj, new object[] { "李四" });

4️⃣ 获取属性并读取/设置值

csharp 复制代码
PropertyInfo prop = type.GetProperty("Name");
string name = (string)prop.GetValue(obj);
prop.SetValue(obj, "王五");

5️⃣ 获取字段并操作

csharp 复制代码
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)委托缓存 来优化。

示例:构建属性访问委托

csharp 复制代码
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();
}
使用:
csharp 复制代码
var getter = CreatePropertyGetter<Person>("Name");
Person p = new Person { Name = "Tom" };
Console.WriteLine(getter(p));  // 输出 Tom

🧩 五、动态代码生成(System.Reflection.Emit)

你可以使用 System.Reflection.Emit 在运行时生成新的类型和方法,用于高性能场景(如 AOP、代理类生成、动态编译)。

示例:动态创建一个类并调用方法

csharp 复制代码
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!

💪 六、实战练习:通用对象克隆器

功能要求:

  • 支持任意类型的对象深拷贝;
  • 使用反射复制所有公共属性;
  • 忽略只读属性;
  • 返回新实例。
示例代码:
csharp 复制代码
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;
}
使用示例:
csharp 复制代码
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 编译器扩展的基本原理。
相关推荐
u_topian4 分钟前
【个人笔记】Qt使用的一些易错问题
开发语言·笔记·qt
没有羊的王K38 分钟前
SSM框架学习——day1
java·学习
珊瑚里的鱼41 分钟前
LeetCode 692题解 | 前K个高频单词
开发语言·c++·算法·leetcode·职场和发展·学习方法
AI+程序员在路上44 分钟前
QTextCodec的功能及其在Qt5及Qt6中的演变
开发语言·c++·qt
xingshanchang1 小时前
Matlab的命令行窗口内容的记录-利用diary记录日志/保存命令窗口输出
开发语言·matlab
Risehuxyc1 小时前
C++卸载了会影响电脑正常使用吗?解析C++运行库的作用与卸载后果
开发语言·c++
时光追逐者1 小时前
C#/.NET/.NET Core技术前沿周刊 | 第 46 期(2025年7.7-7.13)
c#·.net·.netcore
AI视觉网奇1 小时前
git 访问 github
运维·开发语言·docker
不知道叫什么呀1 小时前
【C】vector和array的区别
java·c语言·开发语言·aigc
mit6.8241 小时前
Why C# and .NET are still relevant in 2025
c#