跟着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 编译器扩展的基本原理。
相关推荐
cfqq19891 小时前
class对象【C#】2025复习
开发语言·c#
QQ_4376643143 小时前
C++多态
开发语言·c++
华科易迅3 小时前
人工智能学习38-VGG训练
人工智能·学习·人工智能学习38-vgg训练
EanoJiang3 小时前
C#学习日志
c#
动能小子ohhh4 小时前
html实现登录与注册功能案例(不写死且只使用js)
开发语言·前端·javascript·python·html
Xiaouuuuua4 小时前
我开源了一套springboot3快速开发模板
java·开发语言·开源
李明一.5 小时前
Java 全栈开发学习:从后端基石到前端灵动的成长之路
java·前端·学习
struggle20255 小时前
DeepForest开源程序是用于 Airborne RGB 机器学习的 Python 软件包
开发语言·python
杜大哥5 小时前
Python:.py文件如何变成双击可执行的windows程序?(版本1)
开发语言·windows·python