2014-10-19 39 views
2

我知道,使用表達是比使用反射一個獲取屬性值快,我想轉換列表中的數據表,我已經用他們兩個,獲取屬性值的表達式不需要比反射更快嗎?

反射經過時間:36毫秒

表達經過時間:2350毫秒

我不知道我做錯了什麼呢?

我曾嘗試下面的代碼:

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

什麼讓你覺得表達式應該更快?表達式使用反射...可以更快地生成和緩存委託並重用它,但是您的代碼每次都要重建和編譯委託,這需要很長時間。 – 2014-10-19 10:54:23

+0

我明白了,我不知道如何緩存它,你可以做一個示例嗎? – sinanakyazici 2014-10-19 11:13:52

回答

3

你是不是比較蘋果和蘋果:你的表情代碼結構和編譯在每次迭代的表達式,在每一次迭代產生扔掉的活動相當數量。另一方面,反射代碼使用CLR設計人員對系統進行的所有優化,只執行必要的操作。

從本質上講,你是比較準備時間+工作時間爲表達對工作時間進行反思。這不是在動作重複10,000次的情況下使用表達式的預期方式:您需要預先準備並編譯您的lambdas,將它們存儲在某種緩存中,然後在每次迭代時根據需要快速檢索它們。實施某種形式的緩存甚至會出你的比較:

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

現在準備的費用在整個萬個電話傳播,反射成爲三種方法的比較慢:

Elapsed Time For Creating : 29 
Elapsed Time For Converting : 84 <-- Reflection 
Elapsed Time For Converting : 53 <-- Expressions 
+0

非常感謝,我明白了,它需要緩存每個屬性。 – sinanakyazici 2014-10-19 11:19:04

0

你多次編譯表達。編譯一次,速度會更快。

如果編譯表達式每次也較快運行時會自動執行。