我知道使用表达式获取属性值比使用反射更快,我想将列表转换为数据表,我已经使用了它们,
反射耗时:36 ms
表达式运行时间:2350 ms
我想知道我在那里做错了什么?
我尝试过以下代码:
public class Foo
{
public long IntCode { get; set; }
public string Name { get; set; }
public string SurName { get; set; }
public int Age { get; set; }
}
class Program
{
static void Main(string[] args)
{
var r = new Random();
var foos = new List<Foo>();
var sw = new Stopwatch();
sw.Start();
for (int i = 0; i < 10000; i++)
{
foos.Add(new Foo { IntCode = r.Next(), Name = Guid.NewGuid().ToString(), SurName = Guid.NewGuid().ToString(), Age = r.Next() });
}
sw.Stop();
Console.WriteLine("Elapsed Time For Creating : {0}", sw.ElapsedMilliseconds);
sw.Restart();
ConvertWithReflection(foos, "IntCode", "Name", "Age");
sw.Stop();
Console.WriteLine("Elapsed Time For Converting : {0}", sw.ElapsedMilliseconds);
sw.Restart();
ConvertWithExpression(foos, "IntCode", "Name", "Age");
sw.Stop();
Console.WriteLine("Elapsed Time For Converting : {0}", sw.ElapsedMilliseconds);
Console.ReadLine();
}
public static object GetValueGetter<T>(object item,string propertyName)
{
var arg = Expression.Parameter(item.GetType(), "x");
Expression expr = Expression.Property(arg, propertyName);
var unaryExpression = Expression.Convert(expr, typeof(object));
var propertyResolver = Expression.Lambda<Func<T, object>>(unaryExpression, arg).Compile();
var value = propertyResolver((T)item);
return value;
}
public static void ConvertWithReflection<T>(IEnumerable<T> list, params string[] columnNames)
{
var t = list.ToList();
if (!t.Any()) return;
var dataTable = new DataTable();
dataTable.Columns.Add("IntCode");
dataTable.Columns.Add("Name");
dataTable.Columns.Add("SurName");
dataTable.Columns.Add("Age");
foreach (var item in t)
{
var dr = dataTable.NewRow();
for (int i = 0; i < dataTable.Columns.Count; i++)
{
var el = columnNames.ElementAtOrDefault(i);
if (el == null)
{
dr[i] = DBNull.Value;
}
else
{
var property = item.GetType().GetProperty(el);
dr[i] = property.GetValue(item, null);
}
}
dataTable.Rows.Add(dr);
}
}
public static void ConvertWithExpression<T>(IEnumerable<T> list, params string[] columnNames)
{
var t = list.ToList();
if (!t.Any()) return;
var dataTable = new DataTable();
dataTable.Columns.Add("IntCode");
dataTable.Columns.Add("Name");
dataTable.Columns.Add("SurName");
dataTable.Columns.Add("Age");
foreach (var item in t)
{
var dr = dataTable.NewRow();
for (var i = 0; i < dataTable.Columns.Count; i++)
{
var el = columnNames.ElementAtOrDefault(i);
if (el == null)
{
dr[i] = DBNull.Value;
}
else
{
dr[i] = GetValueGetter<T>(item, el);
}
}
dataTable.Rows.Add(dr);
}
}
}
最佳答案
您并不是在比较苹果与苹果:您的表达式代码在每次迭代中构造并编译表达式,从而在每次迭代中产生相当数量的废弃事件。另一方面,反射代码使用 CLR 设计者已放入系统中的所有优化,仅执行必要的操作。
本质上,您正在比较表达的准备时间+工作时间与反射(reflection)的工作时间。当操作重复 10,000 次时,这不是使用表达式的预期方式:您需要预先准备和编译 lambda,将它们存储在某种缓存中,然后在每次迭代时根据需要快速检索它们。实现某种缓存将使您的比较更加平衡:
public static object GetValueGetter<T>(object item, string propertyName, IDictionary<string,Func<T,object>> cache) {
Func<T, object> propertyResolver;
if (!cache.TryGetValue(propertyName, out propertyResolver)) {
var arg = Expression.Parameter(item.GetType(), "x");
Expression expr = Expression.Property(arg, propertyName);
var unaryExpression = Expression.Convert(expr, typeof (object));
propertyResolver = Expression.Lambda<Func<T, object>>(unaryExpression, arg).Compile();
cache.Add(propertyName, propertyResolver);
}
return propertyResolver((T)item);
}
调用看起来像这样:
var cache = new Dictionary<string,Func<T,object>>();
foreach (var item in t) {
var dr = dataTable.NewRow();
for (var i = 0; i < dataTable.Columns.Count; i++) {
var el = columnNames.ElementAtOrDefault(i);
if (el == null) {
dr[i] = DBNull.Value;
} else {
dr[i] = GetValueGetter<T>(item, el, cache);
}
}
dataTable.Rows.Add(dr);
}
既然准备成本分散在 10,000 个调用中,反射就成为三种方法中较慢的一个:
Elapsed Time For Creating : 29
Elapsed Time For Converting : 84 <-- Reflection
Elapsed Time For Converting : 53 <-- Expressions
关于c# - 表达式获取属性值不需要比反射更快吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26449701/