Home > Blockchain >  How do I create an expression tree for Enumerable.Any<T>(IEnumerable<T>, Func<T,bool&
How do I create an expression tree for Enumerable.Any<T>(IEnumerable<T>, Func<T,bool&

Time:08-25

I am trying to create an Expression Tree for Enumerable.Any(IEnumerable, Func<T,bool>)

Actually, I have a list and need to check whether this list has at least 1 element that contains "test"

So it looks very simple:

List<string> strings = new List<string> { "element1", "element2", "element3" };
var test = strings.Any(x => x.Contains("test"));

I am trying to create an Expression Tree and GetMethod returns null, so I think I miss something

Here is a test code:

List<string> strings = new List<string> { "element1", "element2", "element3" };
var testString = "test";
ParameterExpression p = Expression.Parameter(typeof(string), "item");
            
Delegate predicate = Expression.Lambda(
Expression.Call(
p,
typeof(string).GetMethod("Contains", new[] { typeof(string) }),
Expression.Constant(testString)),
p).Compile();
Type predType = typeof(Func<,>).MakeGenericType(typeof(string), typeof(bool));

// Enumerable.Any<T>(IEnumerable<T>, Func<T,bool>)
System.Reflection.MethodInfo anyMethod = typeof(Enumerable).GetMethod("Any", new Type[] {typeof(IEnumerable<string>), predType});
    
Expression anyCall = Expression.Call(
    anyMethod,
    Expression.Constant(strings),
    Expression.Constant(predicate));    

// test
Func<bool> a = (Func<bool>) Expression.Lambda(anyCall).Compile();

Thanks!

CodePudding user response:

Try the following:

var p = Expression.Parameter(typeof(string), "x");
var strParam = Expression.Parameter(typeof(string), "str");
            
// x.Contains(str)
var boyd = Expression.Call(p, nameof(string.Contains), Type.EmptyTypes, strParam);

// x => x.Contains(str)
var lambdaExpression =  Expression.Lambda<Func<string, bool>>(body, p);

var enumerableParam = Expression.Parameter(typeof(IEnumerable<string>), "e");

// e.Any(x => x.Contains(str))
var anyCall = Expression.Call(typeof(Enumerable), nameof(Enumerable.Any), new[]{ typeof(string) }, enumerableParam, lambdaExpression);

// e => e.Any(x => x.Contains(str))
var anyLambda = Expression.Lambda<Func<IEnumerable<string>, string, bool>>(anyCall, enumerableParam, strParam)

// Func<IEnumerable<string>, string, bool>>
var complied = anyLambda.Compile();

// test

List<string> strings = new List<string> { "element1", "element2", "element3" };
var testString = "test";

var result = complied(strings, testString);

CodePudding user response:

(Disclaimer: I am the author of the library in question.)

Using the ExpressionTreeToString library, available on NuGet, you can call the ToString extension method on an expression:

List<string> strings = new List<string> { "element1", "element2", "element3" };
Expression<Func<bool>> expr = () => strings.Any(x => x.Contains("test"));
Console.WriteLine(expr.ToString("Factory methods", "C#"));

which produces output like the following:

var strings = Parameter(
    typeof(List<string>),
    "strings"
);
var x = Parameter(
    typeof(string),
    "x"
);

Lambda(
    Call(
        typeof(Enumerable).GetMethod("Any", new[] { typeof(IEnumerable<string>), typeof(Func<string, bool>) }),
        strings,
        Lambda(
            Call(x,
                typeof(string).GetMethod("Contains", new[] { typeof(string) }),
                Constant("test")
            ),
            x
        )
    )
)

and then modify it for your needs. For example, you could replace the strings variable with a ConstantExpression wrapping the list.

  • Related