有时将 Linq 表达式视为一种在类似于 C# 但不完全是 C# 的东西中构建代码的方法会很有帮助。这是其中之一。
以下代码是使用表达式的Math.Max(int a, int b) 的实现。 return 语句没有像 C# 中那样的快捷方式。您必须创建标签。
// (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) 从示例代码中可以看出,如果不使用标签目标,就无法从函数中“返回”。正如@Grax 暗示的那样,Expression.Goto、Expression.Continue、Expression.Break、Expression.Return 都返回GotoExpressions,该功能几乎相同。