长期反复通过反射读写属性值的最优实现方案咨询
重复反射操作属性的高效替代方案
普通反射的低效核心在于每次调用都会重复扫描元数据、执行类型检查,针对长期重复操作的场景,最优思路是提前缓存关键反射信息或生成可直接执行的代码委托,避免重复开销。以下是几种落地方案:
1. 缓存PropertyInfo实例
这是最易实现的优化方式,直接复用获取到的PropertyInfo,跳过重复的元数据查找:
// 全局缓存,按「类型+属性名」唯一标识存储 private static readonly Dictionary<(Type Type, string PropertyName), PropertyInfo> _propertyCache = new(); // 获取属性值 public static object GetPropertyValue(object obj, string propertyName) { var key = (obj.GetType(), propertyName); if (!_propertyCache.TryGetValue(key, out var propInfo)) { propInfo = key.Type.GetProperty(propertyName); _propertyCache[key] = propInfo; } return propInfo.GetValue(obj); } // 设置属性值 public static void SetPropertyValue(object obj, string propertyName, object value) { var key = (obj.GetType(), propertyName); if (!_propertyCache.TryGetValue(key, out var propInfo)) { propInfo = key.Type.GetProperty(propertyName); _propertyCache[key] = propInfo; } propInfo.SetValue(obj, value); }
性能提升明显,适合属性操作频率中等的场景,代码维护成本极低。
2. 表达式树生成强类型委托
通过表达式树在运行时编译出直接操作属性的委托,性能接近手写代码调用,适合频繁操作属性的场景:
// 缓存编译后的获取委托 private static readonly Dictionary<(Type Type, string PropertyName), Func<object, object>> _getterCache = new(); // 缓存编译后的设置委托 private static readonly Dictionary<(Type Type, string PropertyName), Action<object, object>> _setterCache = new(); // 获取属性值 public static object GetPropertyValueFast(object obj, string propertyName) { var key = (obj.GetType(), propertyName); if (!_getterCache.TryGetValue(key, out var getter)) { var param = Expression.Parameter(typeof(object), "obj"); var castParam = Expression.Convert(param, key.Type); var propertyAccess = Expression.Property(castParam, propertyName); var castResult = Expression.Convert(propertyAccess, typeof(object)); getter = Expression.Lambda<Func<object, object>>(castResult, param).Compile(); _getterCache[key] = getter; } return getter(obj); } // 设置属性值 public static void SetPropertyValueFast(object obj, string propertyName, object value) { var key = (obj.GetType(), propertyName); if (!_setterCache.TryGetValue(key, out var setter)) { var objParam = Expression.Parameter(typeof(object), "obj"); var valueParam = Expression.Parameter(typeof(object), "value"); var castObj = Expression.Convert(objParam, key.Type); var castValue = Expression.Convert(valueParam, key.Type.GetProperty(propertyName).PropertyType); var propertySet = Expression.Assign(Expression.Property(castObj, propertyName), castValue); setter = Expression.Lambda<Action<object, object>>(propertySet, objParam, valueParam).Compile(); _setterCache[key] = setter; } setter(obj, value); }
这种方案性能优于缓存PropertyInfo,且无需了解底层IL指令,平衡了性能与实现复杂度。
3. 动态生成IL指令
这是性能极致的方案,直接生成IL代码操作属性,执行效率几乎和手写属性调用一致,但实现复杂度高,适合对性能有极端要求的场景:
private static readonly Dictionary<(Type Type, string PropertyName), Func<object, object>> _ilGetterCache = new(); public static object GetPropertyValueIl(object obj, string propertyName) { var key = (obj.GetType(), propertyName); if (!_ilGetterCache.TryGetValue(key, out var getter)) { var propInfo = key.Type.GetProperty(propertyName); var getMethod = propInfo.GetGetMethod(); var dynamicMethod = new DynamicMethod( $"Get_{key.Type.Name}_{propertyName}", typeof(object), new[] { typeof(object) }, key.Type.Module); var il = dynamicMethod.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Castclass, key.Type); il.Emit(OpCodes.Callvirt, getMethod); if (propInfo.PropertyType.IsValueType) { il.Emit(OpCodes.Box, propInfo.PropertyType); } il.Emit(OpCodes.Ret); getter = (Func<object, object>)dynamicMethod.CreateDelegate(typeof(Func<object, object>)); _ilGetterCache[key] = getter; } return getter(obj); }
需要对IL指令有基础了解,维护成本较高,但能达到原生调用的性能水准。
4. 借助第三方库简化实现
如果不想自己实现复杂的表达式树或IL逻辑,可以使用成熟的第三方库(如FastMember),它内部封装了上述高效方案,API简洁:
// 初始化类型访问器 var accessor = TypeAccessor.Create(obj.GetType()); // 获取属性值 var value = accessor[obj, propertyName]; // 设置属性值 accessor[obj, propertyName] = newValue;
库会自动缓存编译后的委托,性能接近表达式树方案,同时大幅降低开发成本。
长期视角的选择建议
- 性能要求一般、追求易维护:优先选择缓存
PropertyInfo - 频繁操作属性、平衡性能与复杂度:选择表达式树生成委托
- 极致性能需求、能接受高维护成本:选择动态生成IL
- 追求开发效率、不想重复造轮子:选择第三方库
内容的提问来源于stack exchange,提问作者VIktoriyaV
相关产品推荐
相关产品推荐

