表達式樹可使用ExPRessions類的靜態工廠方法來創建。這種用API的方式創建給予我們在編程極大的靈活性,MSDN上關于表達式的例子也不少,但在使用過程中還是會遇到許多麻煩,對有的表達式類,介紹得不是太清楚。這里把一些常見的表達示類的使用整理了下。BinaryExpression類: 是表示包含二元運算符的表達式。 比如構建形如 (100+88)是個典型的 a+b 式的二元計算,表達式代碼如下
BinaryExpression binaryexp = Expression.MakeBinary (ExpressionType.Add, Expression.Constant(100), Expression.Constant(88));Console.WriteLine(Expression.Lambda(binaryexp).Compile().DynamicInvoke());
輸出: 188
MakeBinary是在Expressions中定義的一個靜態工廠方法(根據ExpressionType來選擇調用),最終調用的是Expression.Add 靜態方法所以是與下面操作等效的
BinaryExpression binaryexp = Expression.Add(Expression.Constant(100), Expression.Constant(88));
a+b的值類型的操作比較簡單,但如果是 "100"+"88" 呢,改下上面的操作
BinaryExpression binaryexp = Expression.Add(Expression.Constant("100"), Expression.Constant("88"));Console.WriteLine(Expression.Lambda(binaryexp).Compile().DynamicInvoke());
但這時會報錯 “沒有為類型“System.String”和“System.String”定義二進制運算符 Add”,這是怎么回事,在我們代碼時如果寫 string s= "100"+"88";是沒錯的啊。實際上字串的 + 在生成IL代碼時,會轉換成 string 的 Concat 擴展方法來操作。因此要實現如 "100"+"88" 還是稍顯麻煩第一步:獲取Concat方法的MethodInfo MethodInfo mif = typeof(string).GetMethods().First(m => m.Name == "Concat" && m.GetParameters().Length ==2); 因為Concat的參數是可變的方法參數,獲取MethodInfo,一定要加上m.GetParameters().Length=實際調用時的參數個數據,不然會出現參數不匹配錯誤。第二步:使用MakeBinary的另一個重載方法
BinaryExpression binaryexp = Expression.Add(Expression.Constant("100"), Expression.Constant("88"), mif); Console.WriteLine(Expression.Lambda(binaryexp).Compile().DynamicInvoke());
輸出: 10088
回頭再看 Expression.Add 的實現
if (!(method == (MethodInfo) null)) return Expression.GetMethodBasedBinaryOperator(ExpressionType.Add, left, right, method, true); if (left.Type == right.Type && TypeUtils.IsArithmetic(left.Type)) return (BinaryExpression) new SimpleBinaryExpression(ExpressionType.Add, left, right, left.Type); else return Expression.GetUserDefinedBinaryOperatorOrThrow(ExpressionType.Add, "op_Addition", left, right, true);
從中看出,當參數年method為null 時,實際上要檢查 TypeUtils.IsArithmetic(left.Type),而IsArithmetic的定義為
internal static bool IsArithmetic(Type type) { type = TypeUtils.GetNonNullableType(type); if (!type.IsEnum) { switch (Type.GetTypeCode(type)) { case TypeCode.Int16: case TypeCode.UInt16: case TypeCode.Int32: case TypeCode.UInt32: case TypeCode.Int64: case TypeCode.UInt64: case TypeCode.Single: case TypeCode.Double: return true; } } return false; }
所以Expression.Add(Expression.Constant("100"), Expression.Constant("88"))在不給定操作方法時,會報錯。當指定操作方法時,表達式雖然用tostring 是 "100"+"88" ,但調用的操作方法是 string.Concat。就象用方法表達式一樣MethodCallExpression methExp = Expression.Call(mif, Expression.Constant("100"), Expression.Constant("88"));因此在調用 Expression.Lambda(binaryexp).Compile().DynamicInvoke();和 Expression.Lambda(methExp).Compile().DynamicInvoke();的結果是一樣的另外從 if (left.Type == right.Type && TypeUtils.IsArithmetic(left.Type)) 前部分看,Add方法是要求類型完全相同,在看上去可行的表達式 Expression.Add(Expression.Constant(100), Expression.Constant(88.0)) (后面是Double類型 ),也會報錯。而就當改為Expression.Add(Expression.Constant((Double)100), Expression.Constant(88.0)) 或 Expression.Add(Expression.Constant(100.0), Expression.Constant(88.0)) 。
新聞熱點
疑難解答