前置知识: C#

C#与反射

4 minAdvanced2026/6/14

反射与表达式树

概述

反射(Reflection)是 C# 在运行时检查和操作型信息的能力。通过反射,你可以在程序运行时获取、方法、属性、字段等成员的信息,甚至动态调用方法、创建实例和修改属性值。表达式树(Expression Trees)则是将代码表示为数据结构的技术,它让你可以在运行时分析、修改和编译代码逻辑。

为什么需要反射?框架和库经常需要在运行时发现和使用型,比如依赖注入容器需要扫描程序集找到服务,序列化库需要读取对象属性来生成 JSON,ORM 需要将数据库列映射到对象属性。表达式树则让 EF Core 能将 LINQ 查询翻译为 SQL,而不是在内存中执行。

基础概念

Type 对象:每个 .NET 型在运行时都有一个对应的 Type 对象,包含该型的所有元数据信息。通过 Type 对象可以获取属性、方法、字段、构造函数等成员。

Assembly:程序集,是 .NET 代码的部署单元。通过 Assembly 可以获取其中定义的所有型。

BindingFlags:控制反射搜索的范围,比如是否包含非公共成员、是否包含静态成员等。

表达式树:将 Lambda 表达式表示为树形数据结构。编译器可以将 Lambda 转换为委托(可执行代码)或表达式树(数据),取决于目标型。

MethodInfo/PropertyInfo/FieldInfo:分别表示方法、属性和字段的元数据,可以通过它们动态调用方法或读写属性。

快速上手

最基本的反射操作:

using System.Reflection;

// 1. 获取 Type 对象的三种方式
Type type1 = typeof(string);                    // 使用 typeof 关键字
Type type2 = "hello".GetType();                 // 从实例获取
Type type3 = Type.GetType("System.String");     // 通过名称获取

// 2. 获取类型信息
Console.WriteLine(type1.Name);          // String
Console.WriteLine(type1.Namespace);     // System
Console.WriteLine(type1.IsClass);       // True
Console.WriteLine(type1.IsValueType);   // False

// 3. 获取并调用方法
var method = typeof(string).GetMethod("Substring", new[] { typeof(int) });
var result = method!.Invoke("Hello World", new object[] { 6 });
Console.WriteLine(result); // World

// 4. 获取和设置属性
public class User
{
    public string Name { get; set; } = "";
    public int Age { get; set; }
}

var user = new User { Name = "张三", Age = 25 };
var prop = typeof(User).GetProperty("Name")!;
Console.WriteLine(prop.GetValue(user)); // 张三
prop.SetValue(user, "李四");
Console.WriteLine(user.Name);           // 李四

详细用法

获取型成员

public class Sample
{
    public string PublicField = "";
    private int _privateField;
    public string Name { get; set; } = "";
    public int Age { get; private set; }
    public void PublicMethod() { }
    private void PrivateMethod() { }
    public static void StaticMethod() { }
}

var type = typeof(Sample);

// 获取所有公共属性
var properties = type.GetProperties();
foreach (var prop in properties)
{
    Console.WriteLine($"属性: {prop.Name}, 类型: {prop.PropertyType.Name}");
}

// 获取所有公共方法
var methods = type.GetMethods();
foreach (var method in methods)
{
    Console.WriteLine($"方法: {method.Name}, 返回类型: {method.ReturnType.Name}");
}

// 获取所有公共字段
var fields = type.GetFields();
foreach (var field in fields)
{
    Console.WriteLine($"字段: {field.Name}, 类型: {field.FieldType.Name}");
}

// 获取非公共成员(使用 BindingFlags)
var privateFields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
foreach (var field in privateFields)
{
    Console.WriteLine($"私有字段: {field.Name}");
}

// 获取静态成员
var staticMethods = type.GetMethods(BindingFlags.Public | BindingFlags.Static);

动态创建实例

public class Product
{
    public string Name { get; set; } = "";
    public decimal Price { get; set; }

    public Product() { }

    public Product(string name, decimal price)
    {
        Name = name;
        Price = price;
    }
}

// 方式一:使用 Activator
var product1 = (Product)Activator.CreateInstance(typeof(Product))!;
var product2 = (Product)Activator.CreateInstance(typeof(Product),
    new object[] { "手机", 3999m })!;

// 方式二:使用构造函数反射
var constructor = typeof(Product).GetConstructor(new[] { typeof(string), typeof(decimal) })!;
var product3 = (Product)constructor.Invoke(new object[] { "电脑", 5999m });

// 方式三:泛型 Activator(更高效)
var product4 = Activator.CreateInstance<Product>();

动态调用方法

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

var calc = new Calculator();

// 调用实例方法
var addMethod = typeof(Calculator).GetMethod("Add")!;
var sum = (int)addMethod.Invoke(calc, new object[] { 3, 5 })!;
Console.WriteLine(sum); // 8

// 调用静态方法
var multiplyMethod = typeof(Calculator).GetMethod("Multiply")!;
var product = (double)multiplyMethod.Invoke(null, new object[] { 2.5, 4.0 })!;
Console.WriteLine(product); // 10

// 调用带 ref/out 参数的方法
var tryParseMethod = typeof(int).GetMethod("TryParse",
    new[] { typeof(string), typeof(int).MakeByRefType() })!;
object[] args = new object[] { "42", 0 };
var success = (bool)tryParseMethod.Invoke(null, args)!;
Console.WriteLine($"解析成功: {success}, 值: {args[1]}"); // 解析成功: True, 值: 42

特性读取

// 自定义特性
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property)]
public class DisplayNameAttribute : Attribute
{
    public string Name { get; }
    public DisplayNameAttribute(string name) => Name = name;
}

[DisplayName("用户信息")]
public class UserModel
{
    [DisplayName("用户名")]
    public string Name { get; set; } = "";

    [DisplayName("年龄")]
    public int Age { get; set; }
}

// 读取类上的特性
var classAttr = typeof(UserModel).GetCustomAttribute<DisplayNameAttribute>();
Console.WriteLine(classAttr?.Name); // 用户信息

// 读取属性上的特性
foreach (var prop in typeof(UserModel).GetProperties())
{
    var propAttr = prop.GetCustomAttribute<DisplayNameAttribute>();
    Console.WriteLine($"{prop.Name} -> {propAttr?.Name}");
}
// Name -> 用户名
// Age -> 年龄

扫描程序集

// 获取当前程序集中所有实现某接口的类型
var types = Assembly.GetExecutingAssembly()
    .GetTypes()
    .Where(t => typeof(IRepository).IsAssignableFrom(t) && !t.IsInterface)
    .ToList();

foreach (var type in types)
{
    Console.WriteLine($"找到仓储实现: {type.Name}");
}

// 加载外部程序集并扫描
var assembly = Assembly.LoadFrom("MyPlugin.dll");
var pluginTypes = assembly.GetTypes()
    .Where(t => typeof(IPlugin).IsAssignableFrom(t) && !t.IsAbstract)
    .ToList();

// 动态创建插件实例
foreach (var pluginType in pluginTypes)
{
    var plugin = (IPlugin)Activor.CreateInstance(pluginType)!;
    plugin.Initialize();
}

表达式树基础

using System.Linq.Expressions;

// 1. Lambda 表达式可以编译为委托或表达式树
Func<int, bool> delegateFunc = x => x > 10;           // 委托
Expression<Func<int, bool>> expressionTree = x => x > 10; // 表达式树

// 2. 表达式树可以编译为委托
var compiled = expressionTree.Compile();
bool result = compiled(15); // True

// 3. 表达式树可以分析结构
Console.WriteLine(expressionTree.Body);              // x > 10
Console.WriteLine(expressionTree.Parameters[0]);     // x

// 4. 手动构建表达式树
// 等价于 x => x > 10
ParameterExpression param = Expression.Parameter(typeof(int), "x");
ConstantExpression constant = Expression.Constant(10, typeof(int));
BinaryExpression body = Expression.GreaterThan(param, constant);
var lambda = Expression.Lambda<Func<int, bool>>(body, param);

// 编译并执行
var func = lambda.Compile();
Console.WriteLine(func(15)); // True
Console.WriteLine(func(5));  // False

表达式树用于动态查询

// 动态构建过滤条件
public static IQueryable<T> WhereDynamic<T>(
    IQueryable<T> source, string propertyName, object value)
{
    var parameter = Expression.Parameter(typeof(T), "x");
    var property = Expression.Property(parameter, propertyName);
    var constant = Expression.Constant(value);
    var equality = Expression.Equal(property, constant);
    var lambda = Expression.Lambda<Func<T, bool>>(equality, parameter);

    return source.Where(lambda);
}

// 使用
var users = new List<User>
{
    new() { Name = "张三", Age = 25 },
    new() { Name = "李四", Age = 30 },
    new() { Name = "王五", Age = 25 }
}.AsQueryable();

// 动态按属性名过滤
var result = WhereDynamic(users, "Name", "张三");
Console.WriteLine(result.Count()); // 1

// 动态按年龄过滤
var age25 = WhereDynamic(users, "Age", 25);
Console.WriteLine(age25.Count()); // 2

表达式树用于属性访问

// 高效的属性获取器(避免每次反射调用)
public static Func<T, object> CreatePropertyGetter<T>(string propertyName)
{
    var parameter = Expression.Parameter(typeof(T), "obj");
    var property = Expression.Property(parameter, propertyName);
    var convert = Expression.Convert(property, typeof(object));
    var lambda = Expression.Lambda<Func<T, object>>(convert, parameter);
    return lambda.Compile();
}

// 使用
var getter = CreatePropertyGetter<User>("Name");
var user = new User { Name = "张三" };
Console.WriteLine(getter(user)); // 张三

// 比直接反射快很多,因为编译后的委托是直接调用

常见场景

简单的对象映射器

public static TTarget MapTo<TSource, TTarget>(TSource source) where TTarget : new()
{
    var target = new TTarget();
    var sourceProps = typeof(TSource).GetProperties();
    var targetProps = typeof(TTarget).GetProperties();

    foreach (var sourceProp in sourceProps)
    {
        // 找到目标类型中同名且类型兼容的属性
        var targetProp = targetProps.FirstOrDefault(p =>
            p.Name == sourceProp.Name &&
            p.PropertyType.IsAssignableFrom(sourceProp.PropertyType));

        if (targetProp is not null && targetProp.CanWrite)
        {
            var value = sourceProp.GetValue(source);
            targetProp.SetValue(target, value);
        }
    }

    return target;
}

// 使用
public class UserDto { public string Name { get; set; } = ""; public int Age { get; set; } }
public class UserViewModel { public string Name { get; set; } = ""; public int Age { get; set; } }

var dto = new UserDto { Name = "张三", Age = 25 };
var vm = MapTo<UserDto, UserViewModel>(dto);
Console.WriteLine(vm.Name); // 张三

插件系统

// 定义插件接口
public interface IPlugin
{
    string Name { get; }
    void Execute();
}

// 加载插件
public class PluginLoader
{
    public List<IPlugin> LoadPlugins(string directory)
    {
        var plugins = new List<IPlugin>();

        foreach (var file in Directory.GetFiles(directory, "*.dll"))
        {
            var assembly = Assembly.LoadFrom(file);
            var pluginTypes = assembly.GetTypes()
                .Where(t => typeof(IPlugin).IsAssignableFrom(t)
                         && !t.IsAbstract
                         && t.GetConstructor(Type.EmptyTypes) is not null);

            foreach (var type in pluginTypes)
            {
                var plugin = (IPlugin)Activator.CreateInstance(type)!;
                plugins.Add(plugin);
            }
        }

        return plugins;
    }
}

注意事项

性能开销反射比直接调用慢几十到上百倍。在性能敏感的场景中,应该缓存反射结果或使用表达式树编译为委托

破坏封装反射可以访问私有成员,但这破坏了型的封装性。只在必要时使用,比如测试或框架开发。

AOT 兼容性:.NET AOT(预先编译)场景下,反射可能不可用或受限。如果目标是 AOT,应使用源生成器替代反射

表达式树的限制:不是所有 C# 语法都能表示为表达式树。语句块、循环、try-catch 等不能直接用表达式树表示。

安全风险反射可以绕过访问修饰符,在处理用户输入时要小心,避免通过反射执行任意代码。

进阶用法

反射 Emit 动态生成

using System.Reflection.Emit;

// 在运行时动态创建一个类
var assemblyName = new AssemblyName("DynamicAssembly");
var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
var moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");

// 定义类型
var typeBuilder = moduleBuilder.DefineType("DynamicClass", TypeAttributes.Public | TypeAttributes.Class);

// 添加属性 Name
var fieldBuilder = typeBuilder.DefineField("_name", typeof(string), FieldAttributes.Private);
var propBuilder = typeBuilder.DefineProperty("Name", PropertyAttributes.None, typeof(string), null);

// 生成 get 方法
var getMethod = typeBuilder.DefineMethod("get_Name",
    MethodAttributes.Public | MethodAttributes.SpecialName, typeof(string), null);
var getIl = getMethod.GetILGenerator();
getIl.Emit(OpCodes.Ldarg_0);
getIl.Emit(OpCodes.Ldfld, fieldBuilder);
getIl.Emit(OpCodes.Ret);
propBuilder.SetGetMethod(getMethod);

// 生成 set 方法
var setMethod = typeBuilder.DefineMethod("set_Name",
    MethodAttributes.Public | MethodAttributes.SpecialName, null, new[] { typeof(string) });
var setIl = setMethod.GetILGenerator();
setIl.Emit(OpCodes.Ldarg_0);
setIl.Emit(OpCodes.Ldarg_1);
setIl.Emit(OpCodes.Stfld, fieldBuilder);
setIl.Emit(OpCodes.Ret);
propBuilder.SetSetMethod(setMethod);

// 创建类型
var dynamicType = typeBuilder.CreateType()!;
var instance = Activator.CreateInstance(dynamicType)!;
dynamicType.GetProperty("Name")!.SetValue(instance, "动态创建");
Console.WriteLine(dynamicType.GetProperty("Name")!.GetValue(instance)); // 动态创建

使用 Source Generator 替代反射

// 传统方式:运行时反射
var properties = typeof(User).GetProperties();
foreach (var prop in properties)
{
    var value = prop.GetValue(user);
    Console.WriteLine($"{prop.Name} = {value}");
}

// 现代方式:编译时代码生成(更快、AOT 友好)
// 使用 [GenerateSerializer] 等特性让源生成器在编译时生成序列化代码
// 运行时无需反射,性能接近手写代码