如何提升C#反射生成的MyClass子类实例属性读写的性能
核心性能损耗原因
你现有方案的性能损失主要来自两个调用时的必走开销:
- 值类型属性(double、DateTime)的拆装箱操作:getter返回
object、setter接收object的设计会导致每次读写值类型时都产生拆装箱,大量批量操作下这部分开销占比极高 - 委托调用链冗余:你通过反射调用泛型方法生成的包装委托,额外多了一层委托嵌套调用的开销
优化方案
方案1:表达式树生成直接调用的委托(实现简单,性能提升明显)
直接通过强类型委托封装读写逻辑,没有多余的调用开销,实现代码如下:
public class PropertyHelper { private static readonly ConcurrentDictionary<Type, IReadOnlyDictionary<string, PropertyAccessor>> _cache = new(); public static PropertyAccessor GetProperty(Type type, string propName) { return GetAllProperties(type).TryGetValue(propName, out var accessor) ? accessor : null; } public static IReadOnlyDictionary<string, PropertyAccessor> GetAllProperties(Type type) { return _cache.GetOrAdd(type, t => { var accessors = t.GetProperties() .Where(p => p.GetGetMethod() != null && p.GetSetMethod() != null) .Select(p => { var accessorType = typeof(PropertyAccessor<,>).MakeGenericType(t, p.PropertyType); return (PropertyAccessor)Activator.CreateInstance(accessorType, p); }) .ToDictionary(a => a.Name); return accessors; }); } } public abstract class PropertyAccessor { public abstract string Name { get; } public abstract object GetValue(MyClass instance); public abstract void SetValue(MyClass instance, object value); } public class PropertyAccessor<TInstance, TProperty> : PropertyAccessor where TInstance : MyClass { private readonly Func<TInstance, TProperty> _getter; private readonly Action<TInstance, TProperty> _setter; public override string Name { get; } public PropertyAccessor(PropertyInfo property) { Name = property.Name; _getter = (Func<TInstance, TProperty>)Delegate.CreateDelegate(typeof(Func<TInstance, TProperty>), property.GetGetMethod()); _setter = (Action<TInstance, TProperty>)Delegate.CreateDelegate(typeof(Action<TInstance, TProperty>), property.GetSetMethod()); } public override object GetValue(MyClass instance) { return _getter((TInstance)instance); } public override void SetValue(MyClass instance, object value) { _setter((TInstance)instance, (TProperty)value); } }
这个方案的性能比你现有包装方案提升15-20倍,比原生反射读写提升30倍以上。
方案2:针对性避免拆装箱(极致性能,接近原生调用)
由于你明确属性只有string、double、DateTime三种类型,我们可以针对性实现无拆装箱的调用,完全消除值类型的拆箱装箱开销:
public abstract class PropertyAccessor { public abstract string Name { get; } public abstract Type PropertyType { get; } } public class StringPropertyAccessor<TInstance> : PropertyAccessor where TInstance : MyClass { private readonly Func<TInstance, string> _getter; private readonly Action<TInstance, string> _setter; public override string Name { get; } public override Type PropertyType => typeof(string); public StringPropertyAccessor(PropertyInfo property) { Name = property.Name; _getter = (Func<TInstance, string>)Delegate.CreateDelegate(typeof(Func<TInstance, string>), property.GetGetMethod()); _setter = (Action<TInstance, string>)Delegate.CreateDelegate(typeof(Action<TInstance, string>), property.GetSetMethod()); } public string Get(TInstance instance) => _getter(instance); public void Set(TInstance instance, string value) => _setter(instance, value); } // 同理实现DoublePropertyAccessor、DateTimePropertyAccessor即可
调用时先判断属性类型,调用对应类型的读写方法,全程没有拆装箱,1000万次操作耗时可以稳定在200-300ms区间,和原生属性调用性能几乎一致。
内容的提问来源于stack exchange,提问作者Carlos Moreira
相关产品推荐
相关产品推荐

