在表达式中使用 LINQ Any

Using LINQ Any in Expression

我必须使用 LINQ 中的表达式来计算一些数据。我创建了这个小测试程序。第一个例子可以找到。如果任何字符串是“abc”,它 return 为 TRUE。

public class ContainProgramClass
{
    static void Main(string[] args)
    {
        ParameterExpression instance = Expression.Parameter(typeof(ContainClass), "item");
        var mainClass = new ContainClass { Data = new List<string> { "abc", "def" } };
        var expression = mainClass.CreateExpression(instance);
        var lambda = Expression.Lambda<Func<ContainClass, bool>>(expression, instance);
        Func<ContainClass, bool> f = lambda.Compile();
        var result = f(mainClass);


        Console.WriteLine($"Result: {result}"); // Result true
        mainClass.Data = new List<string> { "foo", "bar" };
        result = f(mainClass);
        Console.WriteLine($"Result: {result}"); // Result false

        Console.ReadKey();
    }
}
public class ContainClass
{
    private static readonly MethodInfo MethodContains = typeof(Enumerable).GetMethods(
            BindingFlags.Static | BindingFlags.Public)
        .Single(m => m.Name == nameof(Enumerable.Contains) && m.GetParameters().Length == 2);

    private static readonly MethodInfo EnumerableCastMethod = typeof(Enumerable).GetMethod("Cast");
    private static MethodInfo GenericContainsMethod = MethodContains.MakeGenericMethod(typeof(object));

    public List<string> Data { get; set; }


    public Expression CreateExpression(Expression instance)
    {
        string propertyName = nameof(Data);

        MemberExpression collectionPropertyAccessor = Expression.Property(instance, propertyName);

        MethodCallExpression genericCollectionPropertyAccessor = Expression.Call(null
            , EnumerableCastMethod.MakeGenericMethod(new[] { typeof(object) })
            , collectionPropertyAccessor);

        var distinctValueConstant = Expression.Constant("abc");

        var containsExpression = Expression.Call(
            ContainClass.GenericContainsMethod,
            genericCollectionPropertyAccessor,
            distinctValueConstant);

        return containsExpression;
    }
}

但是,如果任何 SUBstring 是“abc”,我希望它 return TRUE。像这样:

public class AnyProgramClass
{
    static void Main(string[] args)
    {
        ParameterExpression instance = Expression.Parameter(typeof(ContainClass), "item");
        var mainClass = new ContainClass { Data = new List<string> { "abcef", "ghi" } };
        var expression = mainClass.CreateExpression(instance);
        var lambda = Expression.Lambda<Func<ContainClass, bool>>(expression, instance);
        Func<ContainClass, bool> f = lambda.Compile();
        var result = f(mainClass);
        Console.WriteLine($"Result: {result}"); // Result true

        mainClass.Data = new List<string> { "abc", "bar" };
        result = f(mainClass);
        Console.WriteLine($"Result: {result}"); // Result true

        mainClass.Data = new List<string> { "foo", "bar" };
        result = f(mainClass);
        Console.WriteLine($"Result: {result}"); // Result false

        Console.ReadKey();
    }
}
public class MainClass1
{
    private static readonly MethodInfo MethodContains = typeof(Enumerable).GetMethods(
            BindingFlags.Static | BindingFlags.Public)
        .Single(m => m.Name == nameof(Enumerable.Contains) && m.GetParameters().Length == 2);
    private static readonly MethodInfo MethodAny = typeof(Enumerable).GetMethods(
            BindingFlags.Static | BindingFlags.Public)
        .Single(m => m.Name == nameof(Enumerable.Any) && m.GetParameters().Length == 2);

    private static readonly MethodInfo EnumerableCastMethod = typeof(Enumerable).GetMethod("Cast");
    private static MethodInfo GenericContainsMethod = MethodContains.MakeGenericMethod(typeof(object));
    private static MethodInfo GenericAnyMethod = MethodAny.MakeGenericMethod(typeof(object));

    public List<string> Data { get; set; }

    public Expression CreateExpression(Expression instance)
    {
        string propertyName = nameof(Data);

        MemberExpression collectionPropertyAccessor = Expression.Property(instance, propertyName);

        // would something like
        // listOfStrings.Any(str => str.Contains("abc"))

        return anyExpression;
    }
}

我真的不知道如何实现 Any(....)。我通常迷失在数据类型中。希望一些表达式专家可以帮助我:)

你可以这样做,例如:

string propertyName = nameof(Data);
MemberExpression collectionPropertyAccessor = Expression.Property(instance, propertyName);
var anyMethod = MethodAny.MakeGenericMethod(typeof(string));
Expression<Func<string, bool>> contains = x => x.Contains("abc");
return Expression.Call(anyMethod, collectionPropertyAccessor, contains);

在这里,您让编译器构建要传递给 Any(..) 调用的表达式。如果这不符合您的需要,您可以手动构建该表达式:

string propertyName = nameof(Data);
MemberExpression collectionPropertyAccessor = Expression.Property(instance, propertyName);
var anyMethod = MethodAny.MakeGenericMethod(typeof(string));
// find string.Contains(string)
var containsMethod = typeof(string).GetMethods(BindingFlags.Instance | BindingFlags.Public).Single(c => c.Name == "Contains" && c.GetParameters().Length == 1 && c.GetParameters()[0].ParameterType == typeof(string));
// new parameter for sub-expression you pass to Any
ParameterExpression x = Expression.Parameter(typeof(string), "x"); 
// (string x) => x.Contains("abc") expression
Expression<Func<string, bool>> contains = Expression.Lambda<Func<string, bool>>(
    Expression.Call(x, containsMethod, Expression.Constant("abc")), x);
return Expression.Call(anyMethod, collectionPropertyAccessor, contains);