定义为 lambda 表达式的 LINQ 表达式是否可以包含其他 LINQ 表达式?
Can a LINQ Expression defined as a lambda expression include other LINQ Expressions?
使用 LINQ 表达式时,C# 编译器会方便地将 C# lambda 转换为表达式对象:
//using System;
//using System.Linq.Expressions;
Expression<Func<int, bool>> lambda_expression = (int x) => x == 3;
这很方便,与显式构造表达式相比可以节省大量输入:
Expression<Func<int, bool>> explicit_expression_object;
{
var x = Expression.Parameter(typeof(int), "x");
explicit_expression =
Expression.Lambda<Func<int, bool>>(Expression.Equal(x, Expression.Constant(3)), x);
}
但是有些情况下需要使用 "longhand" 表达式对象语法,例如在 运行 时动态创建表达式。因此,我目前发现自己混合使用 "expression lambdas" 和动态生成的 "explicit" 表达式对象。
是否可以将表达式对象 "include" 或 "embed" 转换为表达式 lambda?
例如:
Expression inner_expression_object = Expression.Constant(3);
Expression<Func<int, bool>> wrapper_expression =
(int x) => x == inner_expression_object.Embed();
使用 ExpressionVisitor
,您可以将对 Expression
扩展方法的调用替换为 Expression
。
首先,您需要一个 ExpressionVisitor
class 来将 Embed
方法调用扩展到它们的值:
public class EmbedVisitor : ExpressionVisitor {
public override Expression Visit(Expression node) {
if (node?.NodeType == ExpressionType.Call) {
var callnode = node as MethodCallExpression;
if (callnode.Method.Name == "Embed" && callnode.Method.DeclaringType == typeof(ExpressionExt))
return callnode.Arguments[0].Evaluate<Expression>();
}
return base.Visit(node);
}
}
那么你需要一个静态的 class 用于所需的扩展方法:
public static class ExpressionExt {
public static T Embed<T>(this Expression e) {
return default(T);
}
public static Expression ExpandEmbed(this Expression orig) => new EmbedVisitor().Visit(orig);
public static T Evaluate<T>(this Expression e) {
//A little optimization for constant expressions
if (e.NodeType == ExpressionType.Constant)
return (T)((ConstantExpression)e).Value;
else
return (T)Expression.Lambda(e).Compile().DynamicInvoke();
}
}
现在您可以使用这些来扩展嵌入的 Expression
值子表达式:
var inner_expression_object = Expression.Constant(3);
Expression<Func<int, bool>> wrapper_expression =
(int x) => x == inner_expression_object.Embed<int>();
var expanded = wrapper_expression.ExpandEmbed();
// Expression<Func<int,bool>> expanded == (int x) => x == 3;
也可以直接嵌入Expression
表达式并展开:
Expression<Func<int,bool>> wrap2 = x => x == Expression.Multiply(Expression.Constant(4), Expression.Constant(8)).Embed<int>();
var expanded2 = wrap2.ExpandEmbed();
// Expression<Func<int,bool>> expanded2 = x => x == 4*8;
使用 LINQ 表达式时,C# 编译器会方便地将 C# lambda 转换为表达式对象:
//using System;
//using System.Linq.Expressions;
Expression<Func<int, bool>> lambda_expression = (int x) => x == 3;
这很方便,与显式构造表达式相比可以节省大量输入:
Expression<Func<int, bool>> explicit_expression_object;
{
var x = Expression.Parameter(typeof(int), "x");
explicit_expression =
Expression.Lambda<Func<int, bool>>(Expression.Equal(x, Expression.Constant(3)), x);
}
但是有些情况下需要使用 "longhand" 表达式对象语法,例如在 运行 时动态创建表达式。因此,我目前发现自己混合使用 "expression lambdas" 和动态生成的 "explicit" 表达式对象。
是否可以将表达式对象 "include" 或 "embed" 转换为表达式 lambda?
例如:
Expression inner_expression_object = Expression.Constant(3);
Expression<Func<int, bool>> wrapper_expression =
(int x) => x == inner_expression_object.Embed();
使用 ExpressionVisitor
,您可以将对 Expression
扩展方法的调用替换为 Expression
。
首先,您需要一个 ExpressionVisitor
class 来将 Embed
方法调用扩展到它们的值:
public class EmbedVisitor : ExpressionVisitor {
public override Expression Visit(Expression node) {
if (node?.NodeType == ExpressionType.Call) {
var callnode = node as MethodCallExpression;
if (callnode.Method.Name == "Embed" && callnode.Method.DeclaringType == typeof(ExpressionExt))
return callnode.Arguments[0].Evaluate<Expression>();
}
return base.Visit(node);
}
}
那么你需要一个静态的 class 用于所需的扩展方法:
public static class ExpressionExt {
public static T Embed<T>(this Expression e) {
return default(T);
}
public static Expression ExpandEmbed(this Expression orig) => new EmbedVisitor().Visit(orig);
public static T Evaluate<T>(this Expression e) {
//A little optimization for constant expressions
if (e.NodeType == ExpressionType.Constant)
return (T)((ConstantExpression)e).Value;
else
return (T)Expression.Lambda(e).Compile().DynamicInvoke();
}
}
现在您可以使用这些来扩展嵌入的 Expression
值子表达式:
var inner_expression_object = Expression.Constant(3);
Expression<Func<int, bool>> wrapper_expression =
(int x) => x == inner_expression_object.Embed<int>();
var expanded = wrapper_expression.ExpandEmbed();
// Expression<Func<int,bool>> expanded == (int x) => x == 3;
也可以直接嵌入Expression
表达式并展开:
Expression<Func<int,bool>> wrap2 = x => x == Expression.Multiply(Expression.Constant(4), Expression.Constant(8)).Embed<int>();
var expanded2 = wrap2.ExpandEmbed();
// Expression<Func<int,bool>> expanded2 = x => x == 4*8;