将 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 |