2011-01-27 23 views
2

由於種種原因,我動態地構造一個C#拉姆達使用表達式樹的設施。例如我可以在運行時做出Func鍵<字符串,布爾>如下面的代碼片段。使用Expression.Call構建lambda表達式不喜歡某些類型爲params?

public static bool myMethod(object obj) { … } 

    // Construct a Func<string,bool> 
    var myMethod = GetType().GetMethod("myMethod"); 
    var lambdaParams = new ParameterExpression[] {Expression.Parameter(typeof (string))}; 
    var callMyMethod = Expression.Call(myMethod, lambdaParams); 
    var lambda = Expression.Lambda(typeof(Func<string,bool>), callMyMethod, lambdaParams); 
    var del = (Func<string,bool>)lambda.Compile(); 
    del("foo"); // works 

但是如果我使用相同的代碼,力圖使函數求< INT,BOOL >或函數求<日期時間,布爾>它吹起來,其中有下面幾個奇怪的例外說明:

// Construct a Func<DateTime,bool> or perhaps a struct type fails... why? 
    var myMethod = GetType().GetMethod("myMethod"); 
    var lambdaParams = new ParameterExpression[] {Expression.Parameter(typeof (DateTime))}; 
    var callMyMethod = Expression.Call(myMethod, lambdaParams); // Blows up here… 

System.ArgumentException: Expression of type 'System.DateTime' cannot be used for parameter of type 'System.Object' of method 'Boolean myMethod(System.Object)' 

所以,串並工作清單<串>作品,但INT32不工作也沒有日期時間。到底是怎麼回事?我不知道C#的深層內部是如何工作的,但我猜測這是因爲int實際上是作爲一個原語處理的,也許是DateTime(作爲一個結構體)以及...

任何與此有關的幫助將會很大讚賞。

感謝, 帕特

+0

也許是因爲他們是結構(值類型),但他們應該自動裝箱... – 2011-01-27 05:47:25

+6

@Charlie:爲什麼要* *他們可以自動裝箱?如果你想要「手工」做一個表達式樹,然後*你*有責任類型代數根據系統的規則制定;它不會爲你解決你的錯誤。 – 2011-01-27 07:25:25

回答

2

據我瞭解,Expression.Call不執行的值類型參數的自動裝箱。我無法找到任何相關文檔,但在this forum page上提及。

一個解決辦法是明確地做表達拳擊轉換與Expression.TypeAs

創建UnaryExpression該 表示的顯式引用或 裝箱轉換其中null是 供給如果轉換失敗。

在你的情況,這應該工作:

var boxedParams = lambdaParams.Select(p => Expression.TypeAs(p, typeof(object))) 
           .ToArray(); 

var callMyMethod = Expression.Call(myMethod, boxedParams); 

(你不需要花哨的lambda表達式如果只有一個參數)

根據實際使用情況,您可能需要根據相關類型是否爲值類型檢查裝箱轉換是否必要。

+0

謝謝。我實際上在Expression上查找了一個Box方法,但沒有意識到TypeAs會執行該方法。表達式樹中的這種細節有沒有好的文檔? – 2011-01-27 14:17:30

1

檢查了這一點:你必須框中的日期時間,日期時間以來未啓用」引用類型!

// Construct a Func<DateTime,bool> 
var myMethod = typeof(Program).GetMethod("myMethod"); 
var param = Expression.Parameter(typeof(DateTime)); 
var boxy = Expression.TypeAs(param, typeof(object)); 

var callMyMethod = Expression.Call(myMethod, boxy); 
var lambda = Expression.Lambda(typeof(Func<DateTime, bool>), callMyMethod, new ParameterExpression[] { param }); 
var del = (Func<DateTime,bool>)lambda.Compile(); 
del(DateTime.Now); // works