運算式樹狀架構
運算式樹狀架構代表類似樹狀架構的資料結構中程式碼,其中每個節點都是一個運算式,例如,方法呼叫或二進位作業 (例如 x < y
)。
如果您使用過 LINQ,您會有豐富的程式庫使用經驗,其中 Func
類型是 API 集合的一部分 (如果您不熟悉 LINQ,您可能會想閱讀 LINQ 教學課程,並在此文章前參閱 Lambda 運算式一文。) 運算式樹狀架構提供更豐富的函式引數互動。
當您建立 LINQ 查詢時,通常會使用 Lambda 運算式來撰寫函式引數。 在一般 LINQ 查詢中,這些函式引數會轉換成編譯器所建立的委派。
您可能已撰寫使用運算式樹狀架構的程式碼。 Entity Framework 的 LINQ API 接受運算式樹狀架構作為 LINQ 查詢運算式模式的引數。 這可讓 Entity Framework 將以 C# 撰寫的查詢,轉譯為在資料庫引擎中執行的 SQL。 另一個範例是 Moq,這是 .NET 的熱門模擬架構。
當您想要有更豐富的互動時,您必須使用「運算式樹狀架構」。 運算式樹狀架構會以您查看、修改或執行的結構來表示程式碼。 這些工具可讓您在執行階段操作程式碼。 您可撰寫程式碼來查看執行中的演算法,或插入新功能。 在更進階的案例中,您會修改執行中的演算法,甚至將 C# 運算式轉譯為其他格式,以便在其他環境中執行。
您可編譯和執行運算式樹狀架構所表示的程式碼。 建置並執行運算式樹狀架構,會啟用可執行程式碼的動態修改作業、在各種資料庫中執行 LINQ 查詢,以及建立動態查詢。 如需 LINQ 中運算式樹狀架構的詳細資訊,請參閱如何使用運算式樹狀架構建置動態查詢。
運算式樹狀架構也用於動態語言執行階段 (DLR) 中,以提供動態語言與 .NET 之間的互通性,並讓編譯器寫入器發出運算式樹狀架構,而不是 Microsoft Intermediate Language (MSIL)。 如需 DLR 的詳細資訊,請參閱 Dynamic Language Runtime 概觀。
您可以根據匿名 Lambda 運算式讓 C# 或 Visual Basic 編譯器建立運算式樹狀架構,也可以使用 System.Linq.Expressions 命名空間以手動建立運算式樹狀架構。
將 Lambda 運算式指派給類型為 Expression<TDelegate> 的變數時,編譯器會發出程式碼,以建置代表 Lambda 運算式的運算式樹狀架構。
C# 編譯器只會從運算式 Lambda (或單行 Lambda) 產生運算式樹狀架構。 其無法剖析陳述式 Lambda (或多行 Lambda)。 如需 C# 中之 Lambda 運算式的詳細資訊,請參閱 Lambda 運算式。
下列程式碼範例示範如何讓 C# 編譯器建立代表 Lambda 運算式 num => num < 5
的運算式樹狀架構。
Expression<Func<int, bool>> lambda = num => num < 5;
您可在程式碼中建立運算式樹狀架構。 若要建置樹狀架構,您可以建立每個節點,並將節點連結至樹狀架構。 如需了解如何建立運算式,請參閱建置運算式樹狀架構一文。
運算式樹狀結構不可變。 若要修改運算式樹狀架構,您必須複製現有運算式樹狀架構並取代其中節點,藉此建構新的運算式樹狀架構。 您可以使用運算式樹狀架構造訪者來周遊現有運算式樹狀架構。 如需詳細資訊,請參閱轉譯運算式樹狀架構一文。
建置運算式樹狀結構之後,您可以執行運算式樹狀架構所表示的程式碼。
限制
某些較新的 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 的運算式、索引 "from end" (
^
) 運算子或範圍運算式 (..
) async
Lambda 運算式或await
運算式,包含await foreach
和await using
- 元組常值、元組轉換、元組
==
或!=
,或with
運算式 - 捨棄 (
_
)、解構指派、模式比對is
運算子,或模式比對switch
運算式 - 在引數上省略
ref
的 COM 呼叫 ref
in
或out
參數、ref
傳回值、out
引數,或ref struct
型別的任何值