2011-03-28 41 views
1

我已經構建了一個反映類型的ToStringBuilder(找到了here)並動態構建Expressions來編譯快速ToString方法。構建表達式以調用StringBuilder.Append(Object)和DateTime的異常

它運作良好,但我剛剛在DateTimes上發現了它的錯誤。它試圖建立一個StringBuilder.Append(Object)打電話DateTime嗆。我是否需要創建一個表達式到框值類型?這最好的做法是什麼?

我創建了以下測試用例來演示失敗。

// passes 
    [Test] 
    public void AppendDateTime() 
    { 
     StringBuilder sb = new StringBuilder(); 
     sb.Append(new DateTime()); 
    } 


    // throws 
    [Test] 
    public void ExpressionAppendDateTime() 
    { 
     ParameterExpression sbArgExpression = Expression.Parameter(typeof(StringBuilder), "sb"); 
     ParameterExpression dateTimeArgExpression = Expression.Parameter(typeof(DateTime), "dateTime"); 

     var appendMethod = typeof(StringBuilder).GetMethod("Append", new[] {typeof(DateTime)}); 
     var call = Expression.Call(sbArgExpression, appendMethod, dateTimeArgExpression); 

     // throws on this line 
     var lambda = Expression.Lambda<Action<StringBuilder, DateTime>>(call, sbArgExpression, dateTimeArgExpression).Compile(); 

     var datetime = new DateTime(); 
     var sb = new StringBuilder(); 
     lambda.Invoke(sb, datetime); 
    } 

唯一的例外是..

System.ArgumentException was unhandled by user code 
    Message=Expression of type 'System.DateTime' cannot be used for parameter of type 'System.Object' of method 'System.Text.StringBuilder Append(System.Object)' 
    Source=System.Core 
    StackTrace: 
     at System.Linq.Expressions.Expression.ValidateOneArgument(MethodBase method, ExpressionType nodeKind, Expression arg, ParameterInfo pi) 
     at System.Linq.Expressions.Expression.ValidateArgumentTypes(MethodBase method, ExpressionType nodeKind, ReadOnlyCollection`1& arguments) 
     at System.Linq.Expressions.Expression.Call(Expression instance, MethodInfo method, IEnumerable`1 arguments) 
     at System.Linq.Expressions.Expression.Call(Expression instance, MethodInfo method, Expression[] arguments) 
     at Tests.TestToStringBuilder.ExpressionAppendDateTime() in 
    InnerException: 

回答

4

解決它,必須使用Expression.TypeAs鍵入非原始值類型作爲Object

[Test] 
    public void ExpressionAppendDateTime() 
    { 
     ParameterExpression sbArgExpression = Expression.Parameter(typeof(StringBuilder), "sb"); 
     ParameterExpression dateTimeArgExpression = Expression.Parameter(typeof(DateTime), "dateTime"); 

     var appendMethod = typeof(StringBuilder).GetMethod("Append", new[] {typeof(DateTime)}); 

     Type t = typeof(DateTime); 
     Expression arg; 
     if (t.IsValueType && !t.IsPrimitive) 
     { 
      arg = Expression.TypeAs(dateTimeArgExpression, typeof(object)); 
     } 
     else 
     { 
      arg = dateTimeArgExpression; 
     } 

     var call = Expression.Call(sbArgExpression, appendMethod, arg); 

     var lambda = Expression.Lambda<Action<StringBuilder, DateTime>>(call, sbArgExpression, dateTimeArgExpression).Compile(); 

     var datetime = new DateTime(); 
     var sb = new StringBuilder(); 
     lambda.Invoke(sb, datetime); 
    }