经过一些基准测试后,我注意到我创建的大多数使用反射的方法都非常非常慢,尤其是在迭代中使用时。我发现了来自 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#。
您可以使用静态通用帮助器类来缓存属性。
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));
}
}
}
如果属性本身可以是不同的类型,那么您需要一个单独的泛型类(具有非泛型基类)来执行您需要执行的操作,然后您可以缓存这些对象。