Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does it mean for a label target to receive a value?

I have a few questions about the System.Linq.Expressions.LabelExpression and its ancillary classes and methods.

1) The documentation for the LabelExpression class reads thus:

Represents a label, which can be put in any Expression context. If it is jumped to, it will get the value provided by the corresponding GotoExpression. Otherwise, it receives the value in DefaultValue. If the Type equals System.Void, no value should be provided.

What does it mean to return a value to a label target? In other words, what does it mean for a label target to receive a value? I've never done that in my life -- pass a value to a label target when I jump to it?

2) While it perfectly makes sense to go to a label target, what does it mean to return to and continue to and break to a label target?

  1. public static GotoExpression Return(LabelTarget target);
  2. public static GotoExpression Continue(LabelTarget target);
  3. public static GotoExpression Break(LabelTarget target)
like image 253
Water Cooler v2 Avatar asked Dec 17 '14 13:12

Water Cooler v2


1 Answers

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.

like image 81
Shlomo Avatar answered Nov 15 '22 15:11

Shlomo