在泛型方法中提取委托

问题描述 投票:0回答:1

经过一些基准测试后,我注意到我创建的大多数使用反射的方法都非常非常慢,尤其是在迭代中使用时。我发现了来自 C# Corner 的帖子,其中展示了如何为反射方法创建委托,因为验证和安全操作仅在创建委托时完成一次,而不是每个循环完成一次。

C# 角帖展示了与此非常相似的代码示例:

public class Item
{
  public int Value { get; set; }
}


List<Item> list = Enumerable.Repeat(new Item(), 10000000).ToList();

int i = 0;
foreach (var element in list)
{
  i = element.Value;
  element.Value = 3; 
}

然后解释说使用反射来代替会严重影响性能的缺点:

PropertyInfo propInfo = typeof(Item).GetProperty("Value");

foreach (var element in list)
{
  i = (int)propInfo.GetValue(element);
  propInfo.SetValue(element, 3);
}

但是,可以通过创建委托来提高性能:

Action<Item, int> setter = (Action<Item, int>)Delegate.CreateDelegate(
    typeof(Action<Item, int>), null,
    typeof(Item).GetProperty("Value").GetSetMethod());

Func<Item, int> getter = (Func<Item, int>)Delegate.CreateDelegate(
    typeof(Func<Item, int>), null,
    typeof(Item).GetProperty("Value").GetGetMethod());

foreach (var element in list)
{
  i = (int)getter(element);
  setter(element, 3);
}

现在我的一些方法具有泛型类型参数,但我还没有找到如何使用泛型参数创建委托以获得类似的性能提升。 示例:假设我有以下扩展方法,并且我想将反射调用移至委托,我该怎么做

public static void SetValues<T>(this List<T> list)
{
  var propInfos = typeof(T).GetProperties().AsSpan();

  for(int i = 0; i < list.Count; i++) 
  {
    var element = list[i];
    for (int j = 0; j < propInfos.Length; j++)
    {

      var propInfo = propInfos[j];
      if (propInfo.PropertyType == typeof(int) && (int)propInfo.GetValue(element) == default)
        propInfo.SetValue(element, i);
    }
  }
}

我已经找到了各种使用

ILGenerator
来执行此操作的解决方案,如这篇 MSDN 博客文章 中所示,但老实说,根据我的口味,这有点抽象。如果可能的话,我想继续使用 C#。

c# generics reflection delegates
1个回答
0
投票

您可以使用静态通用帮助器类来缓存属性。

public static void SetValues<T>(this List<T> list)
{
    for(int i = 0; i < list.Count; i++) 
    {
        var element = list[i];
        foreach (var prop in GenericHelper<T>.Properties)
        {
            if (prop.getter(element) == default)
                prop.setter(element, i);
        }
    }
}

static class GenericHelper<T>
{
    public static List<(Func<T, int> getter, Action<T, int> setter)> Properties = new();

    static GenericHelper
    {
        foreach (var prop in typeof(T).GetProperties())
        {
            if (prop.PropertyType != typeof(int) || !prop.CanRead || !prop.CanWrite || prop.GetIndexParameters().Length > 0)
                continue;
            var getter = prop.GetMethod.CreateDelegate<Func<T, int>>();
            var setter = prop.SetMethod.CreateDelegate<Action<T, int>>();
            Properties.Add((getter, setter));
        }
    }
}

如果属性本身可以是不同的类型,那么您需要一个单独的泛型类(具有非泛型基类)来执行您需要执行的操作,然后您可以缓存这些对象。

© www.soinside.com 2019 - 2024. All rights reserved.