title | description | keywords | author | manager | ms.date | ms.topic | ms.prod | ms.technology | ms.devlang | ms.assetid |
---|---|---|---|---|---|---|---|---|---|---|
Framework Types Supporting Expression Trees |
Framework Types Supporting Expression Trees |
.NET, .NET Core |
BillWagner |
wpickett |
06/20/2016 |
article |
.net-core |
.net-core-technologies |
dotnet |
e9c85021-0d36-48af-91b7-aaaa66f22654 |
Previous -- Expression Trees Explained
There is a large list of classes in the .NET Core framework that work with Expression Trees. You can see the full list here. Rather than run through the full list, let's understand how the framework classes have been designed.
In language design, an expression is a body of code that evaluates and returns a value. Expressions
may be very simple: the constant expression 1
returns the constant value of 1. They may be more
complicated: The expression (-B + Math.Sqrt(B*B + 4 * A * C)) / (2 * A)
returns one root for a
quadratic equation (in the case where the equation has a solution).
One of the complexities of working with expression trees is that many different
kinds of expressions are valid in many places in programs. Consider an assignment
expression. The right hand side of an assignment could be a constant value, a variable,
a method call expression, or others. That language flexibility means that you may encounter
many different expression types anywhere in the nodes of a tree when you traverse an
expression tree. Therefore, when you can work with the base expression type, that's
the simplest way to work. However, sometimes you need to know more.
The base Expression class contains a NodeType
property for this purpose.
It returns an ExpressionType
which is an enumeration of possible expression types.
Once you know the type of the node, you can cast it to that type, and perform
specific actions knowing the type of the expression node. You can search for certain
node types, and then work with the specific properties of that kind of expression.
For example, this code will print the name of a variable for a variable access expression. I've followed the practice of checking the node type, then casting to a variable access expression and then checking the properties of the specific expression type:
Expression<Func<int, int>> addFive = (num) => num + 5;
if (addFive.NodeType == ExpressionType.Lambda)
{
var lambdaExp = (LambdaExpression)addFive;
var parameter = lambdaExp.Parameters.First();
Console.WriteLine(parameter.Name);
Console.WriteLine(parameter.Type);
}
The System.Linq.Expression
class also contains many static methods to create expressions. These
methods create an expression node using the arguments supplied for its children. In this way,
you build an expression up from its leaf nodes. For example, this code builds an Add expression:
// Addition is an add expression for "1 + 2"
var one = Expression.Constant(1, typeof(int));
var two = Expression.Constant(2, typeof(int));
var addition = Expression.Add(one, two);
You can see from this simple example that many types are involved in creating and working with expression trees. That complexity is necessary to provide the capabilities of the rich vocabulary provided by the C# language.
There are Expression node types that map to almost all of the syntax elements of the C# language. Each type has specific methods for that type of language element. It's a lot to keep in your head at one time. Rather than try to memorize everything, here are the techniques I use to work with Expression trees:
- Look at the members of the
ExpressionType
enum to determine possible nodes you should be examining. This really helps when you want to traverse and understand an expression tree. - Look at the static members of the
Expression
class to build an expression. Those methods can build any expression type from a set of its child nodes. - Look at the
ExpressionVisitor
class to build a modified expression tree.
You'll find more as you look at each of those three areas. Invariably, you will find what you need when you start with one of those three steps.