将 getting/setting 值的性能提高到通过反射创建的 class 创建的对象的属性
Improve performance on getting/setting values to properties on objects created from class created by reflection
我在寻找解决这个非常具体的问题的方法时遇到了一些问题。我有一个 class,我们称它为“MyClass”,它作为我们通过运行时反射创建的许多其他实现的基础。我们创建的这些实现是 classes,它们继承了“MyClass”并向其添加了一堆新的字符串、双精度和 DateTime 属性(我们称它们为 MyClassCustom)。为了在 MyClassCustom 上访问这些新属性的值,我们在基础 class 上有一些方法可以执行一些反射恶作剧来获取数据(更具体地说是来自 GetType().GetProperty(name) 的 SetValue 和 GetValue)。
由于我们正在尝试对这些实现进行批量操作,因此我们担心性能。我已经导航到其他一些问题,比如这个问题,发现如果我使用委托,性能几乎与我使用直接访问器相同(10mi 项目的平均 200ms 与之前的 4~10s 平均)并且它会看起来有点像这样:
Type type = myClassCustomObject.GetType();
Action<MyClass, object> setter = (Action<MyClass, double>)Delegate.CreateDelegate(typeof(Action<MyClass, double>), null, type.GetProperty(propertyName).GetSetMethod());
Func<MyClass, double> getter = (Func<MyClass, double>)Delegate.CreateDelegate(typeof(Func<MyClass, object>), null, type.GetProperty(propertyName).GetGetMethod());
var value = getter(myClassImplementation);
setter(myClassImplementation, value + 1);
但是这不起作用(这是因为我无法将 Action 转换为 Action)并且当我尝试将其包装起来时,我失去了所有性能,就好像我仍在执行 GetProperty(name).SetValue。请参阅下面我们测试的代码:
public class PropertyHelper {
private static readonly ConcurrentDictionary<Type, PropertyHelper[]> Cache = new ConcurrentDictionary<Type, PropertyHelper[]>();
private static readonly MethodInfo CallInnerGetDelegateMethod = typeof(PropertyHelper).GetMethod(nameof(CallInnerGetDelegate), BindingFlags.NonPublic | BindingFlags.Static);
private static readonly MethodInfo CallInnerSetDelegateMethod = typeof(PropertyHelper).GetMethod(nameof(CallInnerSetDelegate), BindingFlags.NonPublic | BindingFlags.Static);
public string Name { get; set; }
public Func<MyClass, object> Getter { get; set; }
public Action<MyClass, object> Setter { get; set; }
public static PropertyHelper GetProperty(Type type, string propName) {
return GetProperties(type).FirstOrDefault(x => x.Name == propName);
}
public static PropertyHelper[] GetProperties(Type type) {
return Cache.GetOrAdd(type, _ => type.GetProperties().Select(property => {
Type eventLogCustomType = property.DeclaringType;
Type propertyType = property.PropertyType;
Func<MyClass, object> getter = null;
var getMethod = property.GetGetMethod();
if (getMethod != null) {
Type getMethodDelegateType = typeof(Func<,>).MakeGenericType(eventLogCustomType, propertyType);
Delegate getMethodDelegate = Delegate.CreateDelegate(getMethodDelegateType, null, getMethod);
MethodInfo callInnerGenericGetMethodWithTypes = CallInnerGetDelegateMethod.MakeGenericMethod(eventLogCustomType, propertyType);
getter = (Func<MyClass, object>)callInnerGenericGetMethodWithTypes.Invoke(null, new[] { getMethodDelegate });
}
Action<MyClass, object> setter = null;
var setMethod = property.GetSetMethod();
if (setMethod != null) {
Type setMethodDelegateType = typeof(Action<,>).MakeGenericType(eventLogCustomType, propertyType);
Delegate setMethodDelegate = Delegate.CreateDelegate(setMethodDelegateType, null, setMethod);
MethodInfo callInnerGenericSetMethodWithTypes = CallInnerSetDelegateMethod.MakeGenericMethod(eventLogCustomType, propertyType);
setter = (Action<MyClass, object>)callInnerGenericSetMethodWithTypes.Invoke(null, new[] { setMethodDelegate });
}
return new PropertyHelper {
Name = property.Name,
Getter = getter,
Setter = setter,
};
}).ToArray());
}
private static Func<MyClass, object> CallInnerGetDelegate<TClass, TResult>(Func<TClass, TResult> innerDelegate)
where TClass : MyClass {
return instance => innerDelegate((TClass)instance);
}
private static Action<MyClass, object> CallInnerSetDelegate<TClass, TResult>(Action<TClass, TResult> innerDelegate)
where TClass : MyClass {
return (instance, value) => innerDelegate((TClass)instance, (TResult)value);
}
}
我想我已经用尽了我在这个问题上的知识,不知道还能去哪里找。
提前致谢。
您可以使用表达式树生成委托,这几乎是您提供的代码的两倍:
using LinqExpression = System.Linq.Expressions.Expression;
private static readonly ConcurrentDictionary<Type, PropertyHelper[]> CacheExpressionTree = new ConcurrentDictionary<Type, PropertyHelper[]>();
public static PropertyHelper[] GetPropertiesExpressionTree(Type type)
{
return CacheExpressionTree.GetOrAdd(type, _ => type.GetProperties().Select(property =>
{
Type eventLogCustomType = property.DeclaringType;
Type propertyType = property.PropertyType;
var instance = LinqExpression.Parameter(typeof(MyClass));
Func<MyClass, object> getter = null;
var getMethod = property.GetGetMethod();
if (getMethod != null)
{
getter =
LinqExpression.Lambda<Func<MyClass, object>>(
LinqExpression.Convert(
LinqExpression.Call(
LinqExpression.Convert(instance, eventLogCustomType),
getMethod),
typeof(object)),
instance)
.Compile();
}
Action<MyClass, object> setter = null;
var setMethod = property.GetSetMethod();
if (setMethod != null)
{
var parameter = LinqExpression.Parameter(typeof(object));
setter =
LinqExpression.Lambda<Action<MyClass, object>>(
LinqExpression.Call(
LinqExpression.Convert(instance, eventLogCustomType),
setMethod,
LinqExpression.Convert(parameter, propertyType)),
instance, parameter)
.Compile();
}
return new PropertyHelper
{
Name = property.Name,
Getter = getter,
Setter = setter,
};
}).ToArray());
}
您也可以查看 FastExpressionCompiler,因为它会提供更多性能:
using LightExpression = FastExpressionCompiler.LightExpression.Expression;
private static readonly ConcurrentDictionary<Type, PropertyHelper[]> CacheExpressionTreeFast = new ConcurrentDictionary<Type, PropertyHelper[]>();
public static PropertyHelper[] GetPropertiesExpressionTreeFast(Type type)
{
return CacheExpressionTreeFast.GetOrAdd(type, _ => type.GetProperties().Select(property =>
{
Type eventLogCustomType = property.DeclaringType;
Type propertyType = property.PropertyType;
var instance = LightExpression.Parameter(eventLogCustomType);
Func<MyClass, object> getter = null;
var getMethod = property.GetGetMethod();
if (getMethod != null)
{
getter = LightExpression.Lambda<Func<MyClass, object>>(LightExpression.Call(instance, getMethod), instance).CompileFast();
}
Action<MyClass, object> setter = null;
var setMethod = property.GetSetMethod();
if (setMethod != null)
{
var parameter = LightExpression.Parameter(propertyType);
setter = LightExpression.Lambda<Action<MyClass, object>>(LightExpression.Call(instance, setMethod, parameter), instance, parameter).CompileFast();
}
return new PropertyHelper
{
Name = property.Name,
Getter = getter,
Setter = setter,
};
}).ToArray());
}
我 运行 比较不同方法的基准,结果如下:
| Method | Mean | Error | StdDev | Median |
|-------------------------------------- |----------:|----------:|----------:|----------:|
| DirectSetter | 5.529 ns | 0.0076 ns | 0.0059 ns | 5.529 ns |
| DirectGetter | 3.127 ns | 0.0092 ns | 0.0072 ns | 3.126 ns |
| GetPropertiesSetter | 15.897 ns | 0.2153 ns | 0.1798 ns | 15.872 ns |
| GetPropertiesGetter | 15.393 ns | 0.3532 ns | 1.0191 ns | 14.942 ns |
| GetPropertiesExpressionTreeSetter | 7.200 ns | 0.1357 ns | 0.1133 ns | 7.174 ns |
| GetPropertiesExpressionTreeGetter | 6.095 ns | 0.1034 ns | 0.0863 ns | 6.070 ns |
| GetPropertiesExpressionTreeFastSetter | 6.621 ns | 0.0101 ns | 0.0084 ns | 6.622 ns |
| GetPropertiesExpressionTreeFastGetter | 5.918 ns | 0.0167 ns | 0.0130 ns | 5.916 ns |
我在寻找解决这个非常具体的问题的方法时遇到了一些问题。我有一个 class,我们称它为“MyClass”,它作为我们通过运行时反射创建的许多其他实现的基础。我们创建的这些实现是 classes,它们继承了“MyClass”并向其添加了一堆新的字符串、双精度和 DateTime 属性(我们称它们为 MyClassCustom)。为了在 MyClassCustom 上访问这些新属性的值,我们在基础 class 上有一些方法可以执行一些反射恶作剧来获取数据(更具体地说是来自 GetType().GetProperty(name) 的 SetValue 和 GetValue)。
由于我们正在尝试对这些实现进行批量操作,因此我们担心性能。我已经导航到其他一些问题,比如这个问题,发现如果我使用委托,性能几乎与我使用直接访问器相同(10mi 项目的平均 200ms 与之前的 4~10s 平均)并且它会看起来有点像这样:
Type type = myClassCustomObject.GetType();
Action<MyClass, object> setter = (Action<MyClass, double>)Delegate.CreateDelegate(typeof(Action<MyClass, double>), null, type.GetProperty(propertyName).GetSetMethod());
Func<MyClass, double> getter = (Func<MyClass, double>)Delegate.CreateDelegate(typeof(Func<MyClass, object>), null, type.GetProperty(propertyName).GetGetMethod());
var value = getter(myClassImplementation);
setter(myClassImplementation, value + 1);
但是这不起作用(这是因为我无法将 Action
public class PropertyHelper {
private static readonly ConcurrentDictionary<Type, PropertyHelper[]> Cache = new ConcurrentDictionary<Type, PropertyHelper[]>();
private static readonly MethodInfo CallInnerGetDelegateMethod = typeof(PropertyHelper).GetMethod(nameof(CallInnerGetDelegate), BindingFlags.NonPublic | BindingFlags.Static);
private static readonly MethodInfo CallInnerSetDelegateMethod = typeof(PropertyHelper).GetMethod(nameof(CallInnerSetDelegate), BindingFlags.NonPublic | BindingFlags.Static);
public string Name { get; set; }
public Func<MyClass, object> Getter { get; set; }
public Action<MyClass, object> Setter { get; set; }
public static PropertyHelper GetProperty(Type type, string propName) {
return GetProperties(type).FirstOrDefault(x => x.Name == propName);
}
public static PropertyHelper[] GetProperties(Type type) {
return Cache.GetOrAdd(type, _ => type.GetProperties().Select(property => {
Type eventLogCustomType = property.DeclaringType;
Type propertyType = property.PropertyType;
Func<MyClass, object> getter = null;
var getMethod = property.GetGetMethod();
if (getMethod != null) {
Type getMethodDelegateType = typeof(Func<,>).MakeGenericType(eventLogCustomType, propertyType);
Delegate getMethodDelegate = Delegate.CreateDelegate(getMethodDelegateType, null, getMethod);
MethodInfo callInnerGenericGetMethodWithTypes = CallInnerGetDelegateMethod.MakeGenericMethod(eventLogCustomType, propertyType);
getter = (Func<MyClass, object>)callInnerGenericGetMethodWithTypes.Invoke(null, new[] { getMethodDelegate });
}
Action<MyClass, object> setter = null;
var setMethod = property.GetSetMethod();
if (setMethod != null) {
Type setMethodDelegateType = typeof(Action<,>).MakeGenericType(eventLogCustomType, propertyType);
Delegate setMethodDelegate = Delegate.CreateDelegate(setMethodDelegateType, null, setMethod);
MethodInfo callInnerGenericSetMethodWithTypes = CallInnerSetDelegateMethod.MakeGenericMethod(eventLogCustomType, propertyType);
setter = (Action<MyClass, object>)callInnerGenericSetMethodWithTypes.Invoke(null, new[] { setMethodDelegate });
}
return new PropertyHelper {
Name = property.Name,
Getter = getter,
Setter = setter,
};
}).ToArray());
}
private static Func<MyClass, object> CallInnerGetDelegate<TClass, TResult>(Func<TClass, TResult> innerDelegate)
where TClass : MyClass {
return instance => innerDelegate((TClass)instance);
}
private static Action<MyClass, object> CallInnerSetDelegate<TClass, TResult>(Action<TClass, TResult> innerDelegate)
where TClass : MyClass {
return (instance, value) => innerDelegate((TClass)instance, (TResult)value);
}
}
我想我已经用尽了我在这个问题上的知识,不知道还能去哪里找。
提前致谢。
您可以使用表达式树生成委托,这几乎是您提供的代码的两倍:
using LinqExpression = System.Linq.Expressions.Expression;
private static readonly ConcurrentDictionary<Type, PropertyHelper[]> CacheExpressionTree = new ConcurrentDictionary<Type, PropertyHelper[]>();
public static PropertyHelper[] GetPropertiesExpressionTree(Type type)
{
return CacheExpressionTree.GetOrAdd(type, _ => type.GetProperties().Select(property =>
{
Type eventLogCustomType = property.DeclaringType;
Type propertyType = property.PropertyType;
var instance = LinqExpression.Parameter(typeof(MyClass));
Func<MyClass, object> getter = null;
var getMethod = property.GetGetMethod();
if (getMethod != null)
{
getter =
LinqExpression.Lambda<Func<MyClass, object>>(
LinqExpression.Convert(
LinqExpression.Call(
LinqExpression.Convert(instance, eventLogCustomType),
getMethod),
typeof(object)),
instance)
.Compile();
}
Action<MyClass, object> setter = null;
var setMethod = property.GetSetMethod();
if (setMethod != null)
{
var parameter = LinqExpression.Parameter(typeof(object));
setter =
LinqExpression.Lambda<Action<MyClass, object>>(
LinqExpression.Call(
LinqExpression.Convert(instance, eventLogCustomType),
setMethod,
LinqExpression.Convert(parameter, propertyType)),
instance, parameter)
.Compile();
}
return new PropertyHelper
{
Name = property.Name,
Getter = getter,
Setter = setter,
};
}).ToArray());
}
您也可以查看 FastExpressionCompiler,因为它会提供更多性能:
using LightExpression = FastExpressionCompiler.LightExpression.Expression;
private static readonly ConcurrentDictionary<Type, PropertyHelper[]> CacheExpressionTreeFast = new ConcurrentDictionary<Type, PropertyHelper[]>();
public static PropertyHelper[] GetPropertiesExpressionTreeFast(Type type)
{
return CacheExpressionTreeFast.GetOrAdd(type, _ => type.GetProperties().Select(property =>
{
Type eventLogCustomType = property.DeclaringType;
Type propertyType = property.PropertyType;
var instance = LightExpression.Parameter(eventLogCustomType);
Func<MyClass, object> getter = null;
var getMethod = property.GetGetMethod();
if (getMethod != null)
{
getter = LightExpression.Lambda<Func<MyClass, object>>(LightExpression.Call(instance, getMethod), instance).CompileFast();
}
Action<MyClass, object> setter = null;
var setMethod = property.GetSetMethod();
if (setMethod != null)
{
var parameter = LightExpression.Parameter(propertyType);
setter = LightExpression.Lambda<Action<MyClass, object>>(LightExpression.Call(instance, setMethod, parameter), instance, parameter).CompileFast();
}
return new PropertyHelper
{
Name = property.Name,
Getter = getter,
Setter = setter,
};
}).ToArray());
}
我 运行 比较不同方法的基准,结果如下:
| Method | Mean | Error | StdDev | Median |
|-------------------------------------- |----------:|----------:|----------:|----------:|
| DirectSetter | 5.529 ns | 0.0076 ns | 0.0059 ns | 5.529 ns |
| DirectGetter | 3.127 ns | 0.0092 ns | 0.0072 ns | 3.126 ns |
| GetPropertiesSetter | 15.897 ns | 0.2153 ns | 0.1798 ns | 15.872 ns |
| GetPropertiesGetter | 15.393 ns | 0.3532 ns | 1.0191 ns | 14.942 ns |
| GetPropertiesExpressionTreeSetter | 7.200 ns | 0.1357 ns | 0.1133 ns | 7.174 ns |
| GetPropertiesExpressionTreeGetter | 6.095 ns | 0.1034 ns | 0.0863 ns | 6.070 ns |
| GetPropertiesExpressionTreeFastSetter | 6.621 ns | 0.0101 ns | 0.0084 ns | 6.622 ns |
| GetPropertiesExpressionTreeFastGetter | 5.918 ns | 0.0167 ns | 0.0130 ns | 5.916 ns |