初學 LINQ 易混淆的五大術語:Expression Tree、Expression、Lambda Expression、Query Expression、Query Operator。本文逐一解析差異與關聯。
| 術語 | 定義與角色 |
|---|---|
| Expression Tree | 表示對 IQueryable<T> 查詢的樹狀結構,由多個 Query Method 巢狀組成,運行期由 LINQ to SQL 轉譯為 SQL |
| Expression | System.Linq.Expressions.Expression 類別,運算式樹每個節點的基類,提供建構各類節點的 Factory Method |
| Lambda Expression | 可作為運算式樹節點(Expression<TDelegate>)或編譯為委托(Func<T>)的語法糖 |
| Query Operator | Enumerable/Queryable 中定義的擴充方法(Where/Select/Join/OrderBy/GroupBy 等) |
| Query Expression | 類 SQL 語法(from/where/select),編譯器將其轉譯為 Query Operator 呼叫 |
對應 context.Customers.Where(c => c.City == "London"):
ConstantExpression customersExpr = Expression.Constant(context.Customers);
ParameterExpression paramExpr = Expression.Parameter(typeof(Customer), "c");
MemberExpression cityExpr = Expression.Property(paramExpr, "City");
BinaryExpression equalExpr = Expression.Equal(cityExpr, Expression.Constant("London"));
LambdaExpression lambdaExpr = Expression.Lambda(equalExpr, paramExpr);
MethodCallExpression whereExpr = Expression.Call(
typeof(Queryable), "Where", new Type[] { typeof(Customer) },
customersExpr, lambdaExpr);
// 輸出:Table(Customer).Where(c => (c.City = "London"))
Quote(expr) 將運算式本身作為值輸出,而非運算結果:
BinaryExpression expr = Expression.Add(Expression.Constant(2), Expression.Constant(3));
var expr1 = Expression.Lambda<Func<int>>(expr); // () => 2 + 3
var expr2 = Expression.Lambda<Func<BinaryExpression>>(Expression.Quote(expr)); // () => (2 + 3)
expr1.Compile()(); // 5
expr2.Compile()(); // (2 + 3) ← 運算式樹結構本身
// 錯誤:無法隱式轉換
LambdaExpression expr = () => 2 + 3;
// 正確:建構運算式樹節點
Expression<Func<int>> expr = () => 2 + 3;
LambdaExpression node = Expression.Lambda(Expression.Add(Expression.Constant(2), Expression.Constant(3)));
// 正確:建構委托
Func<int> fn = () => 2 + 3;
Func<int> fn2 = () => { return 2 + 3; }
並非所有 Query Operator 皆有 Query Expression 對應,僅支援:
Select / SelectMany / Join / GroupJoin / Where /
OrderBy / OrderByDescending / GroupBy / ThenBy / ThenByDescending
因 Query Operator 為擴充方法,可自行實作覆蓋預設行為:
public static class QueryOperatorExt {
public static IQueryable<T> Where<T>(this IQueryable<T> source, Expression<Func<T, bool>> predicate) {
Console.WriteLine("Calling custom Where");
return Queryable.Where(source, predicate);
}
}
// 使用 Query Expression 時會呼叫自訂 Where
result1 = context.Customers.Where(condition); vs result2 = context.Customers.Where(condition.Compile()); 執行結果同為 6,但差異何在?答案見「LINQ那些事(8)」。