首页 > 解决方案 > 在 C# 中与嵌套表达式树作斗争

问题描述

我正在尝试在 C# 中创建一个表达式树来动态表示以下嵌套的 lambda ...

item => selectorList.Any(selector => terms.Any(sTerm => selector.Contains(sTerm))

在哪里:

经过大量工作,我已经到了这里——但现在我正在努力把它做好。我缺少对嵌套时如何将 ParameterExpression 用作解析值的一些基本理解。

 public static Expression<Func<T, bool>> CreateWhereAnyContainsLambaExpression<T>(IEnumerable<string> terms, params Expression<Func<T, String>>[] selectorList)
    {
        // Create ParameterExpressions
        ParameterExpression qi = Expression.Parameter(typeof(T), "qi");
        ParameterExpression selector = Expression.Parameter(typeof(Expression<Func<T, String>>), "selector");
        ParameterExpression sTerm = Expression.Parameter(typeof(string), "sTerm");

        // Create ConstantExpressions
        ConstantExpression termsConstant = Expression.Constant(terms);
        ConstantExpression selectorListConstant = Expression.Constant(selectorList);

        // Get MethodInfo
        MethodInfo selectorListAny = typeof(Enumerable).GetMethods().Where(m => m.Name == "Any").First(m => m.GetParameters().Count() == 2).MakeGenericMethod(typeof(Expression<Func<T, string>>));
        MethodInfo termsAny = typeof(Enumerable).GetMethods().Where(m => m.Name == "Any").First(m => m.GetParameters().Count() == 2).MakeGenericMethod(typeof(string));
        MethodInfo selectorContains = typeof(string).GetMethod("Contains", new[] { typeof(string) });

        // Build the Expression from inside to out....
        //      selector.Contains(sTerm)
        var expSelectorContains = Expression.Call(selector, selectorContains, sTerm);
        //      sTerm => ...expSelectorContains...
        var sTermLambda = Expression.Lambda<Func<string, bool>>(expSelectorContains, sTerm);
        //      terms.Any(...sTermLambda...)
        var expTermsAny = Expression.Call(termsConstant, termsAny, sTermLambda);
        //      selector => ...expTermsAny...
        var selectorLambda = Expression.Lambda<Func<Expression<Func<T, string>>, bool>>(expTermsAny, selector);
        //      selectorList.Any(...selectorLambda...)
        var expSelectorListAny = Expression.Call(selectorListConstant, selectorListAny, selectorLambda);
        //      item => ...expSelectorListAny...
        var lambda = Expression.Lambda<Func<T, bool>>(expSelectorListAny, qi);
        
        return lambda;
    }

失败就在这条线上

//      selector.Contains(sTerm)
var expSelectorContains = Expression.Call(selector, selectorContains, sTerm);

我在哪里得到异常

System.ArgumentException : Method 'Boolean Contains(System.String)' declared on type 'System.String' cannot be called with instance of type 'System.Linq.Expressions.Expression`1[System.Func`2[TestObject,System.String]]'

鉴于 sTerm 是树的更深处的参数表达式,我如何将其解析表示传递给 Contains 方法?

标签: c#lambdaexpression-trees

解决方案


所以让我澄清一下我造成的混乱......我正在selector.Contains(sTerm)用这条线构造 lambdavar expSelectorContains = Expression.Call(selector, selectorContains, sTerm);

现在selectorContainsMethodInfo 预计将针对string. 但是 myselector是 ParameterExpression 类型的Expression<Func<T, String>>

我只是将它与传入的 selectorList 变量相匹配,因为我应该将它与减少时解析的内容相匹配......这只是一个字符串。

所以解决办法是改变

ParameterExpression selector = Expression.Parameter(typeof(Expression<Func<T, String>>), "selector");

ParameterExpression selector = Expression.Parameter(typeof(string), "selector");

嗬!我的错。衷心感谢您的建议。


推荐阅读