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);
关键说明
- 类型转换处理:通过
Expression.Convert分别将输入的object类型参数转换为目标对象类型和属性的实际类型,自动适配值类型与复杂类型的装箱/拆箱操作。 - 合法性检查:提前校验属性是否存在、是否可写,避免运行时异常。
- 性能特性:委托编译仅需一次,缓存后调用性能接近手写Lambda,远优于反射调用。
内容的提问来源于stack exchange,提问作者Tono Nam
相关产品推荐
相关产品推荐

