![](/img/trans.png)
[英]How cast Expression<Func<T1,bool>> to Expression<Func<T2,bool>>?
[英]How to combine expressions Expression<Func<T1, T2, bool>> to a single Expression<Func<T2, bool>>
我有一個由兩個函數組成的條件列表:
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。
我是構建表達式的新手,有人能指出我正確的方向嗎?
您的Expression<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。
可以解決這個問題,但我建議首先使用System.Linq.Expressions.Expression 中的工廠方法將SelectionCriteria
中的每個元素映射到與標准評估相對應的表達式。 然后,您可以將這些表達式組合成一個BinaryExpression ,然后您可以將其包裝在一個LambdaExpression甚至一個Expression 中。
它可能看起來像這樣(做出一些假設):
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));
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.