我目前正在执行一些动态过滤/排序,并且认为进行基准测试以查看情况是个好主意。
首先,这是创建充当“ getter”的表达式的方法:
public static Expression<Func<TEntity, object>> GetPropertyGetter(string propertyName, bool useCache = false)
{
if (useCache && _propertyGetters.ContainsKey(propertyName))
return _propertyGetters[propertyName];
var entityType = typeof(TEntity);
var property = entityType.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance);
if (property == null)
throw new Exception($"Property {propertyName} was not found in entity {entityType.Name}");
var param = Expression.Parameter(typeof(TEntity));
var prop = Expression.Property(param, propertyName);
var convertedProp = Expression.Convert(prop, typeof(object));
var expr = Expression.Lambda<Func<TEntity, object>>(convertedProp, param);
if (useCache)
{
_propertyGetters.Add(propertyName, expr);
}
return expr;
}
这里是基准:
public class OrderBy
{
private readonly List<Entry> _entries;
public OrderBy()
{
_entries = new List<Entry>();
for (int i = 0; i < 1_000_000; i++)
{
_entries.Add(new Entry($"Title {i}", i));
}
}
[Benchmark(Baseline = true)]
public List<Entry> SearchTitle()
{
return _entries.AsQueryable().OrderByDescending(p => p.Title).ToList();
}
[Benchmark]
public List<Entry> SearchTitleDynamicallyWithoutCache()
{
var expr = DynamicExpressions<Entry>.GetPropertyGetter("Title");
return _entries.AsQueryable().OrderByDescending(expr).ToList();
}
[Benchmark]
public List<Entry> SearchTitleDynamicallyWithCache()
{
var expr = DynamicExpressions<Entry>.GetPropertyGetter("Title", useCache: true);
return _entries.AsQueryable().OrderByDescending(expr).ToList();
}
}
public class Entry
{
public string Title { get; set; }
public int Number { get; set; }
public Entry(string title, int number)
{
Title = title;
Number = number;
}
}
所以我的问题是,为什么创建一个表达式(使用反射来获取属性)要比直接访问(p => p.Title
)更快?