我对System.Linq.Expressions.LabelExpression
及其辅助类和方法有几个问题。
1)LabelExpression
类的文档如下:
表示一个标签,可以放在任何Expression上下文中。如果跳转到,它将获得相应GotoExpression
提供的值。否则,它会收到DefaultValue
中的值。如果Type
等于System.Void
,则不应提供任何值。
将值返回给标签目标是什么意思?换句话说,标签目标接收值意味着什么?我一生中从未这样做过 - 当我跳到它时,将值传递给标签目标?
2)虽然转到标签目标是完全有意义的,但返回和继续和<是什么意思em>打破标签目标?
答案 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
必须具有类型和值,因此默认值的类型LabelTarget
和GotoExpression
必须匹配。原始示例代码使用0作为默认值,但正如您所看到的,永远不会使用它。如果您为0
或0.0
切换null
,则.Compile()
来电时,表达式将失败。
2)从示例代码中可以看出,没有办法返回&#39;没有使用标签目标的功能。正如@Grax暗示的那样,Expression.Goto
,Expression.Continue
,Expression.Break
,Expression.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";
}
解决第二个问题:你总是“去”一个标签目标。 “返回”,“继续”和“休息”是你如何“去”目标的风格。返回意味着标签目标位于方法的末尾,并将返回值传递给它。继续并断开意味着标签目标正在参与循环。