由於各種原因,我正在使用表達式樹設施動態構建C#lambda。例如,我可以在運行時創建一個Func <string,bool>,如下面的代碼片段所示。
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
但是,如果我使用相同的代碼來嘗試創建一個Func <int,bool>或一個Func <DateTime,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)'
因此,字符串工作和List <string>工作,但int32不起作用,也不工作DateTime。到底是怎麼回事?我不知道C#的深層內部是如何工作的,但我猜它是由於int真的被處理為一個原語,也許是DateTime(作為一個結構)...
任何有關這方面的幫助將不勝感激。
謝謝,帕特
據我了解, Expression.Call
不執行值類型參數的自動裝箱。我無法找到任何相關的文檔,但在此論壇頁面上提到了它 。
一種解決方法是使用Expression.TypeAs
在表達式中顯式執行裝箱轉換。
創建一個UnaryExpression,表示顯式引用或裝箱轉換,如果轉換失敗,則提供null。
在你的情況下,這應該工作:
var boxedParams = lambdaParams.Select(p => Expression.TypeAs(p, typeof(object)))
.ToArray();
var callMyMethod = Expression.Call(myMethod, boxedParams);
(如果只有一個參數,你不需要花哨的lambda)
根據實際使用情況,您可能必須檢查是否需要裝箱轉換,具體取決於所討論的類型是否為值類型。
看看這個:你必須打包DateTime,因為DateTime不是一個參考類型!
// 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