2009-07-07 100 views
0

我工作的問題類似Question 222511我確實需要使用MemberInit表達式,所以我可以將它們添加到構造函數中...我試圖實現John Skeet's答案,但我遇到了一個大性能差異。下面是一些代碼:與MemberInit表達式的性能差異

// Method A: 
// This work good, is fast and returns an un-executed query... 
DataContext.LoanNote.Join<LoanNote, Customer, int, LoanNote>(
    DataContext.Customers, loanNote => loanNote.PrimaryCustomerNumber, customer => customer.CustomerNumber, 
(LoanNote loanNote, Customer customer) => new LoanNote() 
{ 
    AccountFeeBillAmount = loanNote.AccountFeeBillAmount, 
    AccountOpenDate = loanNote.AccountOpenDate, 
    // This goes on and on... 
    PrimaryCustomer = customer 
}); 

// Method B: 
// This on the other hand is a lot slower and I am not sure why... 
var resultSelector = BuildJoinResultSelector<LoanNote, Customer, LoanNote("PrimaryCustomer").Compile(); 

DataContext.LoanNote.Join<LoanNote, Customer, int, LoanNote>(
     DataContext.Customers, loanNote => loanNote.PrimaryCustomerNumber, customer => customer.CustomerNumber, resultSelector); 


// The build MemberInitExpression method... 
private static Expression<Func<TOuter, TInner, TResult>> BuildJoinResultSelector<TOuter, TInner, TResult>(string propertyName) where TResult : class 
     { 
      var result = default(Expression<Func<TOuter, TInner, TResult>>); 
      var resultType = typeof(TResult); 
      var outerType = typeof(TOuter); 
      var innerType = typeof(TInner); 
      var outer = Expression.Parameter(outerType, "outer"); 
      var inner = Expression.Parameter(innerType, "inner"); 
      var bindings = new List<MemberBinding>(); 

      foreach (var property in resultType.GetProperties()) 
      { 
       if (property.CanRead == false) 
       { 
        continue; 
       } 
       else if (property.CanWrite == false) 
       { 
        continue; 
       } 
       else if (property.Name == propertyName) 
       { 
        var condition = Expression.Condition(Expression.Equal(inner, Expression.Constant(null)), Expression.New(innerType), inner); 

        bindings.Add(Expression.Bind(property, condition)); 
       } 
       else 
       { 
        bindings.Add(Expression.Bind(property, Expression.Property(outer, property))); 
       } 
      } 

      var memberInit = Expression.MemberInit(Expression.New(resultType), bindings); 

      result = Expression.Lambda<Func<TOuter, TInner, TResult>>(memberInit, outer, inner); 

      return result; 
     } 
+0

你在你的標題有錯字(performa * n * ce) – SLaks 2009-07-07 17:15:04

+0

您可以發佈方法A的完整代碼嗎? – SLaks 2009-07-07 17:24:07

回答

1

第二種方法會更慢執行,因爲它使用反射(在GetProperties呼叫)。

如果調用了很多次,你可以緩存的GetProperties,像這樣的結果:

static class PropertiesCache<T> { 
    public static readonly PropertyInfo[] Properties = typeof(T).GetProperties(); 
} 

這將每個呼叫類型GetProperties只有一次;使用這樣的:

foreach (var property in PropertiesCache<TResult>.Properties) { 
    if(!property.CanRead || !property.CanWrite) continue; 

    //... 
} 

編輯

您還可以使用LINQ查詢替換整個循環,就像這樣:

var memberInit = Expression.MemberInit(Expression.New(typeof(TResult)), 
    from property in PropertiesCache<TResult>.Properties 
    where property.CanRead && property.CanWrite 
    select Expression.Bind(property, property.Name == propertyName ? 
     Expression.Coalesce(inner, Expression.New(innerType)) 
     : Expression.Property(outer, property) 
     ) 
    );