C# 元编程:从反射到代码生成

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

C# 元编程:当代码编写代码



想象一下,你是一位建筑师,建造的不是一座房子,而是一个建筑机器人,它能自己砌墙、铺设电线、粉刷外墙。在 C# 的世界里,这个“机器人”就是元编程——编写能够创建、分析或修改其他程序(或自身)的程序的艺术。



为什么需要它?想象一下,你正在编写几十个用于处理 JSON 的同类类,创建复杂的插件系统,或者懒得手动通过反射调用数百个方法。C# 中的元编程可以自动化这些繁琐工作,创建灵活的框架,并成倍地加速开发。今天,我们将剖析这项技术的三大支柱:反射、表达式树和动态代码生成。



本文适合那些已经能熟练编写 C# 代码,但希望深入了解 CLR 内部机制,并学习区分高级开发者与中级开发者的“魔法”的读者。我们开始吧!



反射:代码的 X 光



反射是 C# 中元编程的基础。它允许你在运行时获取类型、方法、属性和字段的信息,并动态调用它们。想象一下,你看着一个对象,就像翻开一本打开的书,可以阅读它的任何一页。



获取类型信息



最简单的方法是使用 typeof 关键字或 GetType() 方法:



Type stringType = typeof(string);Console.WriteLine($"名称: {stringType.Name}"); // StringConsole.WriteLine($"命名空间: {stringType.Namespace}"); // SystemConsole.WriteLine($"是类吗? {stringType.IsClass}"); // True


动态调用方法



假设你有一个 Calculator 类,并且你想调用它的 Add 方法,但方法名只在运行时才知道(例如,从配置中获取):



public class Calculator{    public int Add(int a, int b) => a + b;}

// 使用反射Calculator calc = new Calculator();Type calcType = typeof(Calculator);MethodInfo? addMethod = calcType.GetMethod("Add");

if (addMethod != null){ int result = (int)addMethod.Invoke(calc, new object[] { 5, 3 }); Console.WriteLine($"结果: {result}"); // 8}


反射何时有用?

  • 序列化 / 反序列化——像 Newtonsoft.Json 这样的库大量使用反射。
  • 插件——加载外部程序集并调用其方法。
  • 测试——访问私有字段和方法(尽管最好避免这样做)。


重要! 反射是一种慢速操作。如果你要调用同一个方法数千次,请考虑缓存 MethodInfo 或使用委托。



表达式树:代码即数据



表达式树是一种将代码表示为数据结构(树)的方式。你可以分析、修改甚至“即时”编译这段代码。这是创建 ORM(Entity Framework)、验证器和动态查询的强大工具。



创建简单的树



与其编写 lambda 表达式 x => x * 2,不如手动构建它:



using System.Linq.Expressions;

// 创建参数 xParameterExpression param = Expression.Parameter(typeof(int), "x");// 创建常量 2ConstantExpression constant = Expression.Constant(2);// 创建乘法 x * 2BinaryExpression body = Expression.Multiply(param, constant);// 将所有内容组合成 lambda:x => x * 2Expression<Func<int, int>> lambda = Expression.Lambda<Func<int, int>>(body, param);

// 编译并执行Func<int, int> compiled

博客

书籍推荐