You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#构建支持值类型与复杂类型的Setter Lambda(替代反射)

构建高性能属性Setter委托(支持值类型与复杂类型)

我需要在应用中验证大量复杂对象,例如以下类结构:

public class School{
   public string Name {get;set;}
   public DateTime DateCreated {get;set;}
   public List<Person> Students {get;set;}
   public Dictionary<string, Teacher> Teachers {get;set;}
   ...
}
public abstract class Human
{...}
public class Person : Human
{...}
public class Student : Person 
{...}
public class Teacher: Person 
{...}

最初我通过递归+反射的方式验证对象属性(如空值、字符串长度合规性),核心逻辑如下:

foreach(var propInfo in myObj.GetType().GetProperties())
{
    // 字符串类型非空校验逻辑
    // 字典类型遍历校验
    // 集合类型遍历元素校验

    if(propInfo.PropertyType.IsClass)
       // 递归调用验证子对象
}

为了提升性能,我计划在程序启动时提前分析所有目标类型并缓存类型信息,同时为每个属性构建高性能的Getter和Setter委托——Getter委托类型为Func<object, object>,我已经实现了对应的构建方法:

Getter委托实现代码

// Getter
private static Func<object, object> BuildGetter(Type type, string propertyName)
{
    // 创建Lambda的输入参数表达式
    ParameterExpression paramExpression = Expression.Parameter(typeof(object), "x");

    // 将输入参数转换为目标类型
    UnaryExpression convertExpression = Expression.Convert(paramExpression, type);

    // 获取目标属性的PropertyInfo
    var propInfo = type.GetProperty(propertyName);
    if (propInfo is null)
    {
        if (Debugger.IsAttached)
            Debugger.Break();
        throw new Exception($"Property {propertyName} does not exist on type {type.Name}");
    }

    // 创建获取属性值的表达式
    MemberExpression propExpression = Expression.Property(convertExpression, propInfo);

    // 将属性值转换为object类型作为返回值
    UnaryExpression resultExpression = Expression.Convert(propExpression, typeof(object));

    // 构建并编译Lambda表达式
    LambdaExpression lambdaExpression = Expression.Lambda(resultExpression, paramExpression);
    Func<object, object> func = (Func<object, object>)lambdaExpression.Compile();

    return func;
}

Getter委托使用示例

// 构建时耗时,但缓存后调用性能极高
Func<object, object> myGetterBuilt = BuildGetter(typeof(School),"Name");
var retrievedValue = myGetterBuilt(school); // 比反射快100倍!

现在我需要实现Setter委托,要求支持int/double/float/bool等值类型以及各种复杂类型,委托类型为Action<object, object>。例如针对School的DateCreated属性,最终生成的Lambda应该等价于:

Action<object,object> mySetter = (object obj, object val) => ((School)obj).DateCreated = (DateTime)val;

Setter委托实现方案

以下是完整的Setter委托构建方法,通过表达式树实现,自动处理值类型与复杂类型的转换:

private static Action<object, object> BuildSetter(Type type, string propertyName)
{
    // 获取目标属性的PropertyInfo
    var propInfo = type.GetProperty(propertyName);
    if (propInfo is null)
    {
        if (Debugger.IsAttached)
            Debugger.Break();
        throw new Exception($"Property {propertyName} does not exist on type {type.Name}");
    }

    // 检查属性是否有Setter方法
    if (!propInfo.CanWrite)
    {
        throw new Exception($"Property {propertyName} on type {type.Name} is read-only");
    }

    // 创建Lambda的两个输入参数:obj(目标对象)、val(要设置的值)
    ParameterExpression objParam = Expression.Parameter(typeof(object), "obj");
    ParameterExpression valParam = Expression.Parameter(typeof(object), "val");

    // 将obj参数转换为目标类型
    UnaryExpression objConvert = Expression.Convert(objParam, type);
    // 将val参数转换为属性的实际类型(处理值类型和复杂类型)
    UnaryExpression valConvert = Expression.Convert(valParam, propInfo.PropertyType);

    // 创建属性赋值表达式:objConvert.Property = valConvert
    BinaryExpression assignExpression = Expression.Assign(Expression.Property(objConvert, propInfo), valConvert);

    // 构建并编译Lambda表达式,返回Action<object, object>委托
    LambdaExpression lambda = Expression.Lambda<Action<object, object>>(assignExpression, objParam, valParam);
    return lambda.Compile();
}

Setter委托使用示例

// 提前构建并缓存Setter委托
Action<object, object> dateCreatedSetter = BuildSetter(typeof(School), "DateCreated");
// 调用Setter设置值
dateCreatedSetter(school, DateTime.Now);

关键说明

  1. 类型转换处理:通过Expression.Convert分别将输入的object类型参数转换为目标对象类型和属性的实际类型,自动适配值类型与复杂类型的装箱/拆箱操作。
  2. 合法性检查:提前校验属性是否存在、是否可写,避免运行时异常。
  3. 性能特性:委托编译仅需一次,缓存后调用性能接近手写Lambda,远优于反射调用。

内容的提问来源于stack exchange,提问作者Tono Nam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 14:07:05