簡體   English   中英

如何組合表達式 Expression <Func<T1, T2, bool> &gt; 到單個表達式<Func<T2, bool> &gt;

[英]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);

嘗試將兩個LambdaExpressionOrElse會在運行時引發異常。

您的代碼甚至沒有編譯,因為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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM