标签目标接收值意味着什么?

时间:2014-12-17 13:55:05

标签: c# .net linq expression-trees linq-expressions

我对System.Linq.Expressions.LabelExpression及其辅助类和方法有几个问题。

1)LabelExpression类的文档如下:

表示一个标签,可以放在任何Expression上下文中。如果跳转到,它将获得相应GotoExpression提供的值。否则,它会收到DefaultValue中的值。如果Type等于System.Void,则不应提供任何值。

将值返回给标签目标是什么意思?换句话说,标签目标接收值意味着什么?我一生中从未这样做过 - 当我跳到它时,将值传递给标签目标?

2)虽然转到标签目标是完全有意义的,但返回继续和<是什么意思em>打破标签目标?

  1. public static GotoExpression Return(LabelTarget target);
  2. public static GotoExpression Continue(LabelTarget target);
  3. public static GotoExpression Break(LabelTarget target)

2 个答案:

答案 0 :(得分:4)

将Linq表达式视为一种在类似C#的东西中构建代码的方法有时会有所帮助,但不完全是C#。这是其中一次。

以下代码是使用表达式的Math.Max(int a, int b)的实现。像C#中的return语句没有捷径。你必须创建标签。

        // (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);

理解LabelExpression为什么需要值的关键:表达式总是被输入(为了我们的目的,void是一种类型),并且几乎总是返回一个值。例如,BlockExpression采用最后一个语句的值。 AssignExpression具有赋值的值。同样,LabelExpression必须返回一个值。与任何类型的GotoExpression一起使用时,永远不会使用该默认值,但以下代码是合法的:

        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()();

...因此需要一个默认值。

由于LabelExpression必须具有类型和值,因此默认值的类型LabelTargetGotoExpression必须匹配。原始示例代码使用0作为默认值,但正如您所看到的,永远不会使用它。如果您为00.0切换null,则.Compile()来电时,表达式将失败。

2)从示例代码中可以看出,没有办法返回&#39;没有使用标签目标的功能。正如@Grax暗示的那样,Expression.GotoExpression.ContinueExpression.BreakExpression.Return都返回GotoExpressions,其功能几乎完全相同。

答案 1 :(得分:1)

标签值的目的似乎是提供返回值。如果查看下面的示例代码,“payload”的返回值将传递给“target”标签,并成为表达式的返回码。我尝试了Expression.Return和Expression.Break并得到了相同的结果。 Expression.Continue没有重载将值传递给Label。

var target = Expression.Label(typeof(string));
var debugPrint = typeof(Debug).GetMethod("Print", new Type[] { typeof(string) });

var expr = Expression.Block(typeof(string),
    new Expression[] {
        Expression.Call(debugPrint,Expression.Constant("Before")),
        Expression.Return(target,Expression.Constant("payload"),typeof(string)),
        //Expression.Break(target,Expression.Constant("payload")),
        Expression.Call(debugPrint,Expression.Constant("During")),
        Expression.Label(target,Expression.Constant("Default")),
    }
);

var result = Expression.Lambda<Func<string>>(expr).Compile()();

此表达式大致相当于以下方法。

string Demo()
{
    Debug.Print("Before");
    return "payload";
    Debug.Print("During");
    return "Default";
}

解决第二个问题:你总是“去”一个标签目标。 “返回”,“继续”和“休息”是你如何“去”目标的风格。返回意味着标签目标位于方法的末尾,并将返回值传递给它。继续并断开意味着标签目标正在参与循环。