Page 46 - MSDN Magazine, September 2019
P. 46

behavior of the compiler, so the generated tree might be different from whatyou’dexpectgiventheoriginalcode(Figure4).Someexamples: Closed-over Variables In order to reference the current value of variables when going in and out of lambda expressions, the compiler creates a hidden class whose members correspond to each of the referenced variables; the lambda expression’s function becomes a method of the class (see “Closed-over Variables”). The corresponding expression tree will render closed-over variables as MemberAccess nodes on the hidden instance. In Visual Basic the $VB$Local_ prefix will also be appended to the variable name. NameOf Operator The result of the NameOf operator is ren-
dered as a Constant string value.
String Interpolation and Boxing Conversion This is resolved
into a call to String.Format and a Constant format string. Because the interpolation expression is typed as a value type—Date (the Visual Basic alias for DateTime)—while the corresponding param- eter of String.Format is expecting an Object, the compiler also wraps the interpolation expression with a Convert node to Object.
Extension Method Calls These are actually calls to module-level methods (static methods in C#), and they’re rendered as such in expression trees. Module-level and Shared methods don’t have an instance, so the corresponding MethodCallExpression’s Object property will return Nothing (or null). If the MethodCallExpres- sion represents an instance method call, the Object property will not be Nothing.
The way extension methods are represented in expression trees highlights an important difference between expression trees and Roslyn compiler syntax trees, which preserve the syntax as is; expression trees are focused less on the precise syntax and more on the underlying operations. The Roslyn syntax tree for an extension method call would look the same as a standard instance method call, not a Shared or static method call.
Conversions When an expression’s type doesn’t match the expected type, and there’s an implicit conversion from the expression’s type, the compiler will wrap the inner ex- pression in a Convert node to the expected type. The Visual Basic compiler will do the same when generating expres- sion trees with an expression whose type implements or inherits from the expected type. For example, in Figure 4, the Count extension method expects an IEnumerable(Of Char), but the actual type of the expression is String.
Constructing Expression Tree Objects II:
Using the Factory Methods
You can also construct expression trees using the Shared (static in C#) factory methods at System.Linq.Expressions. Expression. For example, to construct the expression tree objects for i.ToString, where i is an Integer in Visual Basic (or an int in C#), use code like the following:
' Imports System.Linq.Expressions.Expression
Dim prm As ParameterExpression = Parameter(GetType(Integer), "i")
Dim expr As Expression = [Call](
prm,
GetType(Integer).GetMethods("ToString", {}) )
Meta Node Types
Most node types represent code operations. However, there are three “meta” node types—node types that provide information about the tree, but do not map directly to code.
ExpressionType.Quote This type of node always wraps a LambdaExpression, and specifies that the LambdaExpression defines a new expression tree, not a delegate. For example, the tree generated from the following Visual Basic code:
Dim expr As Expression(Of Func(Of Func(Of Boolean))) = Function() Function() True
or the following C# code:
Expression<Func<Func<bool>>> expr = () => () => true;
represents a delegate that produces another delegate. If you want to represent a delegate that produces another expression tree, you have to wrap the inner one in a Quote node. The compiler does this automatically with the following Visual Basic code:
Dim expr As Expression(Of Func(Of Expression(Of Func(Of Boolean)))) = Function() Function() True
Or the following C# code:
Expression<Func<Expression<Func<bool>>>> expr = () => () => true;
ExpressionType.DebugInfo This node emits debug information, so when you debug compiled expressions, the IL at a particular point can be mapped to the right place in your source code.
ExpressionType.RuntimeVariablesExpression Consider the arguments object in ES3; it’s available within a function without having been declared as an explicit variable. Python exposes a locals function, which returns a dictionary of variables defined in the local namespace. These “virtual” variables are described within an expression tree using the RuntimeVariablesExpression.
38 msdn magazine
.NET Development
Figure 4 Compiler-Generated Trees vs. Source Code


































































































   44   45   46   47   48