C# Expression Trees Cookbook
Expression trees are a powerful feature of C# that allow you to manipulate code as data. They can be used for various purposes, such as creating dynamic queries, implementing custom LINQ providers, or performing runtime compilation. In this post, I will share some interesting techniques I had experienced when working with expression trees. If you’d like to add, please comment below.
If you want to create code on the fly, expression trees are a cool way to do it. They are like mini-programs that you can build and run in C#. They take care of a lot of details for you, so you don’t have to worry about things like CIL or MSIL. They can also make your code faster and more efficient than using reflection or dynamic methods. Expression trees are awesome!
Coding with expression trees is a fascinating and fun way to explore the power of programming languages. Expression trees are data structures that represent code as a tree of nodes, where each node can be an operator, a literal value, a variable or a function call. By creating expression trees, you can manipulate code as data and dynamically generate and execute new code at runtime. This means that you are writing an algorithm to create expression trees on one hand, and creating an algorithm to run something else on the other hand. Both at the same time! How cool is that? 😎
Interesting Facts
- Result of an expression is the last expression in the block. This means that you can write concise and elegant code that returns a value without using a return statement. For example, you can write a lambda expression like this:
(x,y) => { x++; y--; x + y; }
and it will return the sum of x and y after incrementing x and decrementing y. if/then/else
are not expressions, meaning they returnvoid
type. This means that you cannot use them in places where an expression is expected, such as in a ternary operator or a switch expression. For example, you cannot write something like this:var result = condition ? if (true) { doSomething(); } else { doSomethingElse(); } : default;
. You have to use an expression instead of anif/then/else
statement.- If you need an expression but can’t think of one yet, use
Expression.Empty()
. This is a handy method that returns an empty expression tree node. You can use it as a placeholder for an expression until you figure out what you want to write. For example, you can write something like this:var expr = Expression.Lambda(Expression.Empty(), parameter);
and then later assign a body to the lambda expression usingexpr.Body = ...;
.
Arrays
Create New
int[] ar = new int[3]
translates to:
var arVar = Expression.Variable(typeof(int[]), "ar");
var x = Expression.Block(new[] { arVar },
Expression.Assign(arVar, Expression.NewArrayBounds(typeof(int), Expression.Constant(3)))
);
Access
Accessing ar[2]
translates to Expression.ArrayAccess(arVar, Expression.Constant(2))
.
Properties
Having a class
class Entity {
public int Id { get; set; }
public int? NullableInt { get; set; }
}
Assign Value
If an instance of Entity
is cVar
then cVar.Id = 3
translates to:
Expression.Assign(Expression.Property(cVar, "Id"), Expression.Constant(3));
and cVar.NullableInt = 3
translates to:
Expression.Assign(Expression.Property(cVar, "NullableInt"), Expression.Convert(Expression.Constant(3), typeof(int?)));
Conditions
If/Then/Else
if(xVar < yVar) {
// on true
} else {
// on false
}
translates to
Expression.IfThenElse(
Expression.LessThan(xVar, yVar),
// on true
Expression.Empty(),
// on false
Expression.Empty());
If you are a fan of functional programming, you might be frustrated by the limitations of the IfThenElse
construct in C#. The problem with IfThenElse
is that it’s actually a statement (return type is void
) and not an expression. Meaning if you need to return something from it, that needs to be wrapped in a block. For instance, to return a boolean flag:
bool flag = false;
if (condition) {
flag = true;
} else {
flag = false;
}
Or in expression syntax:
ParameterExpression flag = Expression.Variable(typeof(bool), "successFlag");
return Expression.Block(
new[] { flag },
Expression.IfThenElse(
Expression.LessThan(xVar, yVar),
Expression.Block(
// do something
Expression.Empty(),
// flag = true
Expression.Assign(flag, Expression.Constant(true))),
// flag = false
Expression.Assign(flag, Expression.Constant(false))),
// return flag
flag);
This is verbose and cumbersome compared to the ternary operator: bool flag = condition ? true : false;
.
Ternary Null Check
I.e. ? :
operator.
x == null ? null : x.y;
translates to:
Expression.Condition(Expression.Equal(x, Expression.Constant(null)),
Expression.Convert(Expression.Constant(null), childType), // cast null to childType as argument types must match
Expression.Property(x, "y"));
One of the coolest features of Condition
is that it can return a value! Unlike IfThenElse
, which only executes a block of code based on a condition and returns nothing (void
), Condition
can evaluate an expression and return either null
or the result of the expression. For example, if you write Condition(x != null, x.y)
, it will return either null
if x
is null, or x.y
if x
is not null. This way, you can use Condition
in places where you need a value, such as assignments, arguments or returns. Isn’t that awesome?
Loops
If you want to create loops with expression trees, you must use Expression.Loop
. This method creates an expression that represents an infinite loop. That’s right, infinite! There is no other way to create loops with expression trees. You might wonder how to exit an infinite loop. Well, you can use Expression.Break
or Expression.Return
to jump out of the loop at any point. Isn’t that cool? Expression trees are a powerful way to represent code as data and manipulate it dynamically. So go ahead and try Expression.Loop
and see what amazing things you can do with it!
Simulating foreach
Let’s simulate the following:
foreach(var element in collection) {
// do something
}
as expression trees. collection
implements IEnumerable<T>
and we’ll refer to T
as elementType
in this code.
Type enumeratorGenericType = typeof(IEnumerator<>).MakeGenericType(elementType);
Type enumerableGenericType = typeof(IEnumerable<>).MakeGenericType(elementType);
ParameterExpression enumeratorVar = Expression.Variable(enumeratorGenericType, "enumerator");
MethodCallExpression getEnumeratorCall = Expression.Call(classesParam,
enumerableGenericType.GetMethod(nameof(IEnumerable.GetEnumerator))!);
MethodCallExpression moveNextCall = Expression.Call(enumeratorVar,
typeof(IEnumerator).GetMethod(nameof(IEnumerator.MoveNext))!);
LabelTarget loopBreakLabel = Expression.Label("loopBreak");
LoopExpression loop = Expression.Loop(
Expression.IfThenElse(
// test
Expression.Equal(moveNextCall, Expression.Constant(true)),
// if true
Expression.Block(
// do something...
),
// if false
Expression.Break(loopBreakLabel)
), loopBreakLabel);
return Expression.Block(
new[] { enumeratorVar, classElementVar },
// get enumerator from class collection
Expression.Assign(enumeratorVar, getEnumeratorCall),
// loop over classes
loop);
Lists
Create new generic list
new List<int>
translates to
Expression.New(typeof(List<int>));
new List<T>
translates to
Expression.New(typeof(List<>).MakeGenericType(typeof(T)));
Declare, Create and Assign
var list = new List<int>();
translates to:
Type listType = typeof(List<int>);
var listVar = Expression.Variable(listType, "list");
var result = Expression.Assign(listVar, Expression.New(listType));
Assign null to List variable
If you are trying to assign null
to list variable in the following way:
Expression.Assign(listVar, Expression.Constant(null));
it won’t work and will fail with an exception:
System.ArgumentException : Expression of type 'System.Object' cannot be used for constructor parameter of type 'System.Collections.Generic.List`1[System.Int32]' (Parameter 'arguments[2]')
The trick is to convert null
to the list type before assigning:
Expression.Assign(listVar, Expression.Convert(Expression.Constant(null), typeof(List<int>)));
which is equivalent to the following C# code:
listVar = (List<int>)null;
Get List element by Index
I.e. elementVar = listVar[idxVar]
will translate to:
Expression.Assign(elementVar, Expression.Property(listVar, "Item", idxVar));
because accessing an indexer in the collection is equivalent to accessing it’s property Item
.
Exceptions
Throw NotImplementedException
when all the parameters are known before compiling:
private static Expression ThrowNotImplementedException(string message) {
return Expression.Throw(Expression.Constant(new NotImplementedException(message)));
}
Tricks
View Expression in Release Mode
If you are a fan of Visual Studio’s DebugView
property, you know how useful it is to see the pseudo code of an expression without having to compile it. But what if you want to use this feature in Release Mode? Unfortunately, DebugView
is an internal property that is only accessible in Debug Mode. However, there is a workaround! You can use this helper extension method to get the DebugView
value of any expression in Release Mode. This way, you can still enjoy the benefits of DebugView
without sacrificing performance or security. Isn’t that awesome?
public static string GetPseudoCode(this Expression expression) {
PropertyInfo? propertyInfo = typeof(Expression).GetProperty("DebugView", BindingFlags.Instance | BindingFlags.NonPublic);
if(propertyInfo == null)
return string.Empty;
return (string)propertyInfo.GetValue(expression)!;
}
Debugging
If you love expression trees as much as I do, you might wonder how to debug them effectively. Well, I have some good news for you! You don’t need to rely on the boring DebugView
property or some external tools to inspect your expression trees. You can create your own pseudo breakpoint by calling a method from your expression tree into one of your classes. How cool is that?
Let me show you how I do it with a static method in my class that generates expression trees:
#if DEBUG
private static void InspectCurrentValues(int dataIdx, int dlIdx, int rlIdx) {
Console.WriteLine($"iteration: D: {dataIdx}, DL: {dlIdx}, RL: {rlIdx}", dataIdx, dlIdx, rlIdx);
}
#endif
Then, in the expression tree itself, I can call this method:
#if DEBUG
Expression.Call(GetType().GetMethod(nameof(InspectCurrentValues),
BindingFlags.NonPublic | BindingFlags.Static)!, _dataIdxVar, _dlIdxVar, _rlIdxVar),
#endif
Breakpoint can be set and will be hit inside the InspectCurrentValues
method.
Tools
- ReadableExpressions.Visualizers for Visual Studio transforms expression tree to a really nice, C#-like view. Compare
DebugView
to the output of this visualiser:
To contact me, send an email anytime or leave a comment below.