lundi 7 août 2023

Extracting Delegates in generic methods

After some benchmarks I noticed most of my methods I created which use reflection are very, very slow, especially when used in iterations. I found a post from C# Corner, which shows how to create delegates for the reflection methods, because the validation and security operations are only done once when creating the delegate instead of each loop.

The C# Corner post show a code example very similar to this:

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; 
}

and then explains that using reflection instead has the drawback of drastically affecting performance:

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

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

performance however can be improved by creating delegates:

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);
}

Now some of my methods have generic type parameters and I haven't found out how to create delegates with generic parameters yet to get a similar performance boost. Example: Let's say I have following extension method and I want to move the reflection calls to delegates, how would I do this?

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);
    }
  }
}

I've found various solutions, that use an ILGenerator to do this, as found in this MSDN Blog Post, but that has honestly been a little to abstract for my taste. I'd like to stay with C#, if possible.





Aucun commentaire:

Enregistrer un commentaire