It is sometimes helpful to think of Linq Expressions as a way to build code in something that resembles C#, but isn't C# exactly. This is one of those times.
The below code is an implementation of a Math.Max(int a, int b)
using Expressions. There is no shortcut for the return
statements like in C#. You have to create labels.
// (a, b =>
// {
// if(a > b)
// return a;
// else
// return b;
// }
var a = Expression.Parameter(typeof(int), "a");
var b = Expression.Parameter(typeof(int), "b");
var returnLabel = Expression.Label(typeof (int));
Expression<Func<int, int, int>> returnMax = (Expression<Func<int, int, int>>)Expression.Lambda
(
Expression.Block
(
Expression.IfThenElse
(
Expression.GreaterThan(a, b),
Expression.Return(returnLabel, a),
Expression.Return(returnLabel, b)
),
Expression.Label(returnLabel, Expression.Constant(0))
),
a,
b
);
var shouldBeSix = returnMax.Compile()(5, 6);
The key to understanding why the LabelExpression
needs a value: Expressions are always typed (for our purposes here, void
is a type), and almost always return a value. A BlockExpression
, for example, take on the value of the last statement. An AssignExpression
takes on the value of assignment. Similarly, a LabelExpression
must return a value. When used in conjunction with a GotoExpression
of any sort, that default value is never used, but the following code is legal:
var returnLabel = Expression.Label(typeof (int));
Expression<Func<int>> returnsSix = (Expression<Func<int>>)Expression.Lambda
(
Expression.Label(
returnLabel,
Expression.Constant(6)
)
);
var alsoSix = returnsSix.Compile()();
... hence the need for a default value.
Since a LabelExpression
must have a type, and a value, the types of the default value, the LabelTarget
and the GotoExpression
all must match. The original sample code uses 0 as a default value, but as you can see, that will never be used. If you switch the 0
for 0.0
or null
, the Expression will fail on the .Compile()
call.
2) As you can see from the sample code, there is no way to 'return' out of a function without using a label target. As @Grax implied, Expression.Goto
, Expression.Continue
, Expression.Break
, Expression.Return
all return GotoExpressions
that function almost identically.
GotoExpressionKind
, which is an enum, which is for informational purposes only. However, I am asking about the meaning of two things: (1) How do you return, continue to, or break to a label target? and (2) How can a label receive a value? – Corrinnecorrival