首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

LINQ 表達式樹基礎:從程式碼到資料結構的轉換藝術

     編輯:關於.NET

初接觸 LINQ 者常覺得表達式樹難懂,實則不然。只要具備基本 LINQ 知識即可輕鬆理解。表達式樹提供「可執行程式碼 → 資料結構」轉換,若需在執行前修改/轉換程式碼 (如 LINQ 查詢轉 SQL) 極具價值。

表達式樹語法

簡單 Lambda 運算式:

Func<int, int, int> function = (a, b) => a + b;

三部分:委托類型宣告、等號、Lambda 運算式。function 指向原生可執行程式碼,等同:

public int function(int a, int b) { return a + b; }

轉換為表達式樹

引入 System.Linq.Expressions:

Expression<Func<int, int, int>> expression = (a, b) => a + b;

expression 非可執行程式碼,而是名為表達式樹的資料結構。

Visual Studio 表達式樹視覺化工具

ExpressionTreeVisualizer 顯示簡單表達式樹
圖 1:VS2008 Samples 中 ExpressionTreeVisualizer 輸出

上半部顯示 Lambda 運算式,下半部 TreeView 顯示組成部分。

程式碼探索表達式樹結構

Expression<TDelegate> 四大屬性:

屬性說明
Body取得表達式主體
Parameters取得 Lambda 參數
NodeType節點類型 (45 種 ExpressionType,如 Add、LessThan、Constant 等)
Type表達式靜態類型 (此例為 Func<int, int, int>)
折疊後的表達式樹顯示四大屬性
圖 2:折疊樹節點清晰顯示四大屬性

探索範例程式碼

// 參數名稱
Console.WriteLine("參數1: {0}, 參數2: {1}", expression.Parameters[0], expression.Parameters[1]);
// 輸出: 參數1: a, 參數2: b

// 主體結構
BinaryExpression body = (BinaryExpression)expression.Body;
ParameterExpression left = (ParameterExpression)body.Left;
ParameterExpression right = (ParameterExpression)body.Right;
Console.WriteLine(expression.Body); // (a + b)
Console.WriteLine("左邊: {0}\n節點類型: {1}\n右邊: {2}\n類型: {3}", left.Name, body.NodeType, right.Name, body.Type);

輸出:

(a + b)
左邊: a
節點類型: Add
右邊: b
類型: System.Int32

編譯表達式樹:資料轉回程式碼

int result = expression.Compile()(3, 5);
Console.WriteLine(result); // 8

Compile() 將表達式樹轉回可執行委托,結果與原始 Lambda 相同。

IQueryable<T> 與表達式樹的關鍵關係

標準 LINQ to SQL 查詢:

var query = from c in db.Customers
            where c.City == "Nantes"
            select new { c.City, c.CompanyName };

query 為 IQueryable 類型,定義:

public interface IQueryable : IEnumerable
{
    Type ElementType { get; }
    Expression Expression { get; }
    IQueryProvider Provider { get; }
}

包含 Expression 屬性 (基類為 Expression<T>),實例擁有相關表達式樹,等同查詢運算式的可執行程式碼資料結構。

複雜 LINQ to SQL 查詢表達式樹視覺化
圖 3:範例 LINQ to SQL 查詢生成的複雜表達式樹

為什麼要轉換成表達式樹?

LINQ to SQL 查詢不在 C# 程式執行,而是轉 SQL、透過網路發送、在資料庫伺服器執行。原始程式碼從未在程式中執行:

// 實際轉換為 SQL 執行:
SELECT [t0].[City], [t0].[CompanyName]
FROM [dbo].[Customers] AS [t0]
WHERE [t0].[City] = @p0

從程式碼 → 字串 (SQL) 傳遞給其他程式。解析表達式樹資料結構遠比解析 IL/原生程式碼容易。編譯器分析表達式樹各部分,決定最佳 SQL 生成策略,理論上可考慮網路狀況、資料庫負載等因素。

IQueryable<T> vs IEnumerable<T>

LINQ to Objects 回傳 IEnumerable<T>,不含 Expression 屬性,極少使用表達式樹。差異在於:

  • 程式碼在程式內執行 → IEnumerable<T>,直接轉 IL 執行
  • 查詢需傳遞給外部程式執行 → IQueryable<T> + 表達式樹,轉換為字串 (SQL、Web Service 呼叫等)

如 LINQ to Amazon 將查詢轉 Web Service 呼叫,同樣使用 IQueryable<T>。程式內執行 (如反射 API 查詢) 仍用 IEnumerable<T>。

總結

  • 表達式樹將可執行程式碼轉為樹狀資料結構,揭示組成部分
  • 核心角色:LINQ to SQL 查詢邏輯的資料抽象,解析生成 SQL 發送伺服器
  • LINQ 讓查詢成為 C# 普通類別,具類型檢查與 IntelliSense
  • 一般規則:程式內執行不需表達式樹;程式外執行利用表達式樹

原文出處:Charlie Calvert《Expression Tree Basics》

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved