c# - 如何组合表达式 Expression> 到单个表达式>
问题描述
我有一个由两个函数组成的条件列表:
public Func<TConfiguration, string> ConfigurationField { get;}
public Func<TNumbering, string> NumberingField { get; }
对于每个条件,表达式如下所示:
Expression<Func<TNumbering, TConfiguration, bool>> (n, c) => criteria.ConfigurationField(c) != criteria.NumberingField(n)
我需要用 OrElse 链接这些表达式的列表。
我尝试做类似的事情:
BinaryExpression expression = null;
foreach (var criteria in SelectionCriteria)
{
Expression<Func<TNumbering, TConfiguration, bool>> exp = (n, c) => criteria.ConfigurationField(c) != criteria.NumberingField(n);
expression = expression == null ? exp : Expression.OrElse(expression, exp);
}
if (expression == null) return Result.Failure("Expression not defined"));
var lambda = Expression.Lambda<Func<TConfiguration, bool>>(expression);
numberingsToRemove = numberings.Where(_ => configurations.All(lambda));
但是,编译器不喜欢它,表示 Expression.Lambda<Func<TConfiguration, bool>> 和二进制表达式之间没有隐式转换。
如果我使用
expression = expression == null ? Expression.OrElse(exp, exp) : Expression.OrElse(expression, exp);
我明白了
没有为类型 'System.Func<TNumbering,TConfiguration,System.Boolean> 和 'System.Func<TNumbering,TConfiguration,System.Boolean> 定义二元运算符 OrElse。
我是构建表达式的新手,有人能指出我正确的方向吗?
解决方案
YourExpression<Func<TNumbering, TConfiguration, bool>>
是一个泛型类型,它的开放泛型类型是Expression<TDelegate>,其中TDelegate是一些委托类型;在这种情况下Func<TNumbering, TConfiguration, bool>
。
Expression<TDelegate>继承自LambdaExpression,它表示一个 C#(或 VB.NET)lambda 表达式。
就像您无法编写以下代码一样:
var result =
(n, c) => criteria.ConfigurationField(c) != criteria.NumberingField(n) ||
(n1, c1) => criteria.ConfigurationField(c1) != criteria.NumberingField(n1);
尝试将两个LambdaExpression组合在一起OrElse
会在运行时引发异常。
您的代码甚至没有编译,因为expression
类型为BinaryExpression,表示与此相对应的表达式:
criteria.ConfigurationField(c) != criteria.NumberingField(n) ||
criteria.ConfigurationField(c1) != criteria.NumberingField(n1)
您尝试将完整的Expression<TDelegate>放入其中,其中包括(例如)参数列表。
每个LambdaExpression都有一个Body属性,它从与此对应的表达式中提取:
(n, c) => criteria.ConfigurationField(c) != criteria.NumberingField(n)
LambdaExpression的主体,或与此相对应的表达式:
criteria.ConfigurationField(c) != criteria.NumberingField(n)
理论上,您可以将其组合成与此相对应的BinaryExpression:
criteria.ConfigurationField(c) != criteria.NumberingField(n) ||
criteria.ConfigurationField(c1) != criteria.NumberingField(n1)
但这也行不通,因为每次迭代都会引入多个新参数,您必须将所有这些参数传递给最终的 lambda。
It's possible to work around this problem, but I would suggest first and foremost you map each element in SelectionCriteria
to an expression corresponding to the criteria evaluation, using the factory methods at System.Linq.Expressions.Expression. You could then combine those expressions into a BinaryExpression which you could then wrap up in a LambdaExpression or even an Expression.
It might look something like this (making some assumptions):
class Criteria<TConfiguration, TNumbering> {
public Func<TConfiguration, string> ConfigurationField { get;}
public Func<TNumbering, string> NumberingField { get; }
}
// using static System.Linq.Expressions.Expression;
var SelectionCritera = new List<Criteria>();
/*
* populate list here
*/
var configParam = Parameter(typeof(TConfiguration));
var numberingParam = Parameter(typeof(TNumbering));
var expressions =
SelectionCriteria.Select(criteria => {
var criteriaExpr = Constant(criteria);
return NotEqual( // !=
Invoke( // ( ... )
PropertyOrField( // .ConfigurationField
criteriaExpr, // criteria
"ConfigurationField"
),
configParam // c
),
Invoke( // ( ... )
PropertyOrField( // .NumberingField
criteriaExpr, // criteria
"NumberingField"
),
numberingParam // n
)
);
})
.ToList();
if (!expressions.Any) { return Result.Failure("Expression not defined")); }
// Combine all the subexpressions using ||
var body = expressions.Aggregate((prev, next) => OrElse(prev, next));
// Create a LambdaExpression
var lmbd = Lambda<Func<TConfiguration, TNumbering, bool>>(body, configParam, numberingParam);
// Create a .NET method from the LambdaExpression
var mthd = lmbd.Compile();
// Apply the method to each config/numbering pair
var result = (
from config in configs
from numbering in numbering
select (config, numbering)
).All(x => mthd(config, numbering));
推荐阅读
- python - Seaborn regplot:点和线合并图例
- wordpress - 所有宽度都在 attr srcset 中指定,但 W3C 验证错误
- python - Python 全局变量导入
- java - QuickSort Java 按降序排列
- swagger - Swagger 编辑器 - 对另一个文件中定义的模型的引用
- python - 如何从列表中表示 x1, y1, x2, y2
- android-studio - 即使在使用 @HiltAndroidApp 注释后,Hilt 也找不到 Application 类
- c# - 如何使用 Stackexchange.Redis 和 Redis 集群
- javascript - 只允许输入字段中的 2 个小数位 - VueJs
- python - 无法从应用程序(未知位置)烧瓶导入应用程序