如何用编译表达式替代直接反射获取对象属性?求等效实现
使用表达式树替代反射获取属性值
当然可以用表达式树编译出等效的逻辑,而且性能比直接反射更好——表达式树编译后生成的是强类型IL代码,避免了每次调用都重复解析反射元数据的开销。下面提供两种实现方式:
方式一:与原反射逻辑签名完全一致的版本
这个版本会动态构建表达式树,完全模拟你原代码的逻辑流程:
public static Func<object, string, object> CreatePropertyGetter() { // 定义表达式的两个输入参数 var objParam = Expression.Parameter(typeof(object), "obj"); var propNameParam = Expression.Parameter(typeof(string), "propName"); // 定义局部变量存储PropertyInfo var propVar = Expression.Variable(typeof(PropertyInfo), "prop"); // 构建表达式块:先获取属性,再判断是否为null,最后返回值 var block = Expression.Block( new[] { propVar }, // 赋值:obj.GetType().GetProperty(propName) Expression.Assign( propVar, Expression.Call( Expression.Call(objParam, typeof(object).GetMethod("GetType")), typeof(Type).GetMethod("GetProperty", new[] { typeof(string) }), propNameParam ) ), // 条件判断:prop不为null则调用GetValue,否则返回null Expression.Condition( Expression.NotEqual(propVar, Expression.Constant(null)), Expression.Convert( Expression.Call( propVar, typeof(PropertyInfo).GetMethod("GetValue", new[] { typeof(object) }), objParam ), typeof(object) ), Expression.Constant(null, typeof(object)) ) ); // 编译表达式为委托 return Expression.Lambda<Func<object, string, object>>(block, objParam, propNameParam).Compile(); }
使用方式和你原代码一致:
var func = CreatePropertyGetter(); var value = func(myObject, "MyProperty");
方式二:带缓存的高性能版本
如果你的代码会频繁获取相同类型的相同属性,建议用这个版本——它会缓存每个(类型, 属性名)组合对应的编译后委托,避免重复编译,性能更优:
private static readonly Dictionary<(Type, string), Func<object, object>> _propertyGetterCache = new Dictionary<(Type, string), Func<object, object>>(); public static object GetPropertyValue(object obj, string propName) { if (obj == null) throw new ArgumentNullException(nameof(obj)); if (string.IsNullOrEmpty(propName)) throw new ArgumentException("属性名不能为空", nameof(propName)); var type = obj.GetType(); var cacheKey = (type, propName); // 先查缓存,缓存命中直接调用 if (!_propertyGetterCache.TryGetValue(cacheKey, out var getter)) { // 构建针对具体类型的属性访问表达式 var objParam = Expression.Parameter(typeof(object), "obj"); // 将object转换为实际类型 var typedObj = Expression.Convert(objParam, type); // 访问属性 var propertyAccess = Expression.Property(typedObj, propName); // 将属性值转换回object var typedValue = Expression.Convert(propertyAccess, typeof(object)); // 编译并缓存委托 getter = Expression.Lambda<Func<object, object>>(typedValue, objParam).Compile(); _propertyGetterCache[cacheKey] = getter; } return getter(obj); }
使用方式:
var value = GetPropertyValue(myObject, "MyProperty");
两种方式都能实现你原反射代码的功能,其中带缓存的版本在重复调用场景下性能远优于直接反射和无缓存的表达式树版本。
内容的提问来源于stack exchange,提问作者JuanDYB
相关产品推荐
相关产品推荐

