Skip to main content
๐Ÿ“œ WAYPOINT LESSON

Anatomy of an expression tree

โญโญโญ advancedโณ 15 min read๐Ÿ“ Lesson 188 of 265

Nodes, visitors, reduction, and what a lambda expression actually becomes as data.

Anatomy of an expression tree

An expression tree is an immutable object graph describing code: every node has a NodeType, a static Type, and children. x => x >= 5 && x < 100 becomes:

Lambda (Func<int, bool>)
โ””โ”€โ”€ AndAlso
    โ”œโ”€โ”€ GreaterThanOrEqual
    โ”‚   โ”œโ”€โ”€ Parameter "x"
    โ”‚   โ””โ”€โ”€ Constant 5
    โ””โ”€โ”€ LessThan
        โ”œโ”€โ”€ Parameter "x"
        โ””โ”€โ”€ Constant 100

Three properties make trees the backbone of LINQ providers:

  1. Inspectable โ€” you can walk and read the logic (that is how EF turns your predicate into SQL).
  2. Rewritable โ€” ExpressionVisitor produces modified copies (that is how providers substitute constants, inline parameters, add tenant filters).
  3. Compilable โ€” Compile() emits a real delegate (that is how the rewritten tree becomes executable again).

The decisive C# rule: a lambda assigned to Expression<TDelegate> is parsed into a tree by the COMPILER; the same lambda assigned to Func<TDelegate>-shaped delegate types is compiled to IL directly. One syntax, two worlds โ€” the target type decides which you get. Note the boundary: only expressions fit; statements, async, and many operators (??=, => bodies with blocks) are outside the tree subset.