表示式樹狀結構 代表類似樹狀數據結構中的程式代碼,其中每個節點都是表達式,例如方法呼叫或二進位作業,例如 x < y
。
如果您使用 LINQ,您有豐富的連結庫體驗,其中 Func
類型是 API 集合的一部分。 (如果您不熟悉 LINQ,您可能想要在此教學課程之前閱讀 LINQ 教學課程和 Lambda 運算式 的相關文章。表達式樹狀架構提供與函式自變數更豐富的互動。
當您建立 LINQ 查詢時,通常會使用 Lambda 表達式撰寫函式自變數。 在典型的 LINQ 查詢中,這些函式自變數會轉換成編譯程式所建立的委派。
您已經撰寫使用表示式樹狀架構的程式代碼。 Entity Framework 的 LINQ API 接受 Expression 樹狀架構作為 LINQ 查詢運算式模式的自變數。 這可讓 Entity Framework 將您在 C# 中撰寫的查詢轉譯成在資料庫引擎中執行的 SQL。 另一個範例是 Moq,這是 .NET 的熱門模擬框架。
當您想要有更豐富的互動時,需要使用 運算式樹狀架構。 表達式樹將程式碼表示為可供您檢查、修改或執行的結構。 這些工具讓您能在運行時操控代碼。 您可以撰寫程式代碼來檢查執行中的演算法,或插入新功能。 在更進階的案例中,您可以修改執行中的演算法,甚至將 C# 運算式轉譯為另一種表單,以在另一個環境中執行。
您可以編譯並執行表示式樹狀架構所代表的程式代碼。 建置和執行表達式樹狀結構可讓您動態修改可執行的程式代碼、在各種資料庫中執行LINQ查詢,以及建立動態查詢。 如需 LINQ 中表示式樹狀架構的詳細資訊,請參閱 如何使用表示式樹狀架構來建置動態查詢。
表達式樹狀架構也用於動態語言執行平臺 (DLR),以提供動態語言與 .NET 之間的互作性,並讓編譯程式寫入器發出表達式樹狀架構,而不是Microsoft中繼語言 (CIL)。 如需 DLR 的詳細資訊,請參閱 動態語言執行平臺概觀。
您可以讓 C# 或 Visual Basic 編譯程式根據匿名 Lambda 運算式為您建立表示式樹狀結構,也可以使用 命名空間手動 System.Linq.Expressions 建立表達式樹狀結構。
將 Lambda 運算式指派給 類型的 Expression<TDelegate>變數時,編譯程式會發出程式代碼來建置代表 Lambda 表達式的運算式樹狀結構。
下列程式代碼範例示範如何讓 C# 編譯程式建立代表 Lambda 運算式 num => num < 5
的表達式樹狀結構。
Expression<Func<int, bool>> lambda = num => num < 5;
您可以在程式代碼中建立表示式樹狀架構。 您可以通過建立每個節點,然後將它們連結成樹狀結構來建構樹。 您將瞭解如何在建置運算式樹一文中建立運算式。
表達式樹狀結構是不可變的。 如果您想要修改表達式樹狀結構,您必須複製現有的表達式樹狀結構並取代其中節點,以建構新的表達式樹狀結構。 您使用表達式樹遍歷器來遍歷現有的表達式樹。 如需詳細資訊,請參閱有關翻譯表達式樹的文章。
建置表達式樹狀結構之後,您會 執行表達式樹狀結構所代表的程序代碼。
局限性
C# 編譯程式只會從表達式 Lambda (或單行 Lambda) 產生表達式樹狀架構。 它無法剖析語句 Lambda(或多行 Lambda)。 如需 C# 中 Lambda 表達式的詳細資訊,請參閱 Lambda 運算式。
有一些較新的 C# 語言元素無法很好地轉譯成表達式樹狀結構。 表達式樹狀架構不能包含 await
表達式或 async
lambda 表達式。 C# 6 和其後版本新增的許多功能,在表達式樹中並未以完全相同的方式出現。 較新的功能反而會在表達式樹中儘可能使用同等的早期語法來呈現。 其他建構無法使用。 這表示當引進新的語言功能時,解譯表達式樹狀架構的程式代碼的運作方式相同。 不過,即使有這些限制,表達式樹狀架構也可讓您建立動態演算法,依賴解譯和修改以數據結構表示的程序代碼。 它可讓 Entity Framework 等豐富連結庫完成其作業。
表達式樹狀結構不支援新的表達式節點類型。 對於解譯表達式樹狀架構的所有連結庫來說,這將是一項重大變更,以引進新的節點類型。 下列清單包含無法使用的大部分 C# 語言元素:
- 從輸出中移除的條件式方法
-
base
存取 - 方法群組表達式,包括方法群組的
&
位址,以及匿名方法表達式 - 本機函式的參考
- 語句,包括指派 (
=
) 和語句主體表達式 - 只有定義宣告的部分方法
- 不安全的指標作業
-
dynamic
操作 -
使用
null
或default
常值左邊的聯合運算符、Null 聯合指派,以及 Null 傳播運算子 (?.
) - 多維度陣列初始化運算式、 索引屬性和字典初始化表達式
- 集合表示式
-
throw
表達式 -
存取
static virtual
或abstract
之介面成員 - 具有屬性的 Lambda 運算式
- 插值字串
- UTF-8 字串轉換或 UTF-8 字串常值
- 使用可變參數、具名參數或可選參數的方法呼叫
- 使用 System.Index 或 System.Range 的表達式,末端索引(
^
)運算符 或 範圍表達式(..
) -
async
Lambda 運算式或await
運算式,包括await foreach
和await using
-
Tuple 常值、Tuple 轉換、Tuple
==
或!=
、with
表示式 -
Discards (
_
)、 解構指派、 模式比對is
運算符或模式比對switch
表達式 - 在參數中省略的 COM 呼叫
ref
-
ref
、in
或out
參數、ref
傳回值、out
引數,或任何ref struct
型別的值