【问题标题】:Convert Contains To Expression Tree将包含转换为表达式树
【发布时间】:2018-03-05 11:09:20
【问题描述】:

相关:Create a Lambda Expression With 3 conditions

请考虑以下代码:

from a in myTbl
where a.Address.Contains(strToCheck)
select a

如何将其转换为表达式树并使用表达式编写上述代码? 主要问题是将a.Address.Contains(strToCheck) 转换为Expression Tree

编辑 1) 地址是 string 字段,strToCheckstring

谢谢

【问题讨论】:

  • .Select()转换为表达式树和.Contains()转换为表达式树有什么区别?没有。所以如果你知道如何转换Select,你应该知道如何转换Contains
  • 谢谢。但是我是表达式树的新手,并且没有很多好的资源。如果你知道答案,请帮助我
  • SO 包含许多示例如何编写调用某些方法的表达式树,例如 Any Where 等等。您可以查看几个示例:firstsecond。主要思想是您从相应的MethodInfo 创建MethodCallExpression。对了,你为什么要把你的例子转换成ExpressionTree
  • 这取决于a.Address 的类型——您需要调用string.ContainsEnumerable.Contains
  • @George Alexandria 请看这个:stackoverflow.com/questions/45673032/… 我应该使用T,但我无法访问T 属性

标签: c# linq lambda expression-trees c#-6.0


【解决方案1】:

a.Address.Contains(strToCheck) 表示对a.Address 上的string.Contains 实例方法 调用 instancestrToCheck 参数。

构建对应表达式的最简单方法是使用以下Expression.Call overload

public static MethodCallExpression Call(
    Expression instance,
    string methodName,
    Type[] typeArguments,
    params Expression[] arguments
)

像这样(使用链接问题中的术语):

var body = Expression.Call(
    Expression.PropertyOrField(param, "Address"), // instance
    "Contains", // method
    Type.EmptyTypes, // no generic type arguments
    Expression.Constant(strToCheck) // argument
);

【讨论】:

  • 它非常棒。谢谢
【解决方案2】:

您没有指定 myTbl 的类型,
因此,我创建了一个仅使用对象列表的简单解决方案。

using System;
using System.Linq;
using System.Linq.Expressions;
using System.Collections.Generic;

namespace Test
{
    class Program
    {
        static void Main(string[] args) {
            var adresses = FilterByAddress("Address", new List<Person> { new Person { Address = "Address1" }, new Person { Address = "AAAAAA" } });
        }

        public static IEnumerable<Person> FilterByAddress(string strToCheck, List<Person> list) {
            var listParam = Expression.Parameter(typeof(IEnumerable<Person>), "list");
            Expression<Func<Person, bool>> contains = a => a.Address.Contains(strToCheck);
            var select = typeof(Enumerable).GetMethods().Single(m => m.Name.Equals("Where") && m.GetParameters()[1].ParameterType.GetGenericArguments().Length == 2);
            var genericMethod = select.MakeGenericMethod(new[] { typeof(Person) });
            var call = Expression.Call(null, genericMethod, new Expression[] { listParam, contains });
            var lambda = Expression.Lambda<Func<IEnumerable<Person>, IEnumerable<Person>>>(call, new[] { listParam });

            return lambda.Compile().Invoke(list);
        }
    }

    public class Person
    {
        public string Address { get; set; }
    }
}

如果您想通过谓词使用过滤器,您可以传递 Expresssion&lt;Func&lt;Person, bool&gt;&gt; 作为参数(一行)

    static void Main(string[] args) {
        var strToCheck = "Address";
        var list = new List<Person> { new Person { Address = "Address1" }, new Person { Address = "AAAAAA" } };
        var adresses = FilterByAddress(list, p => p.Address.Contains(strToCheck));
    }

    public static IEnumerable<Person> FilterByAddress(List<Person> list, Expression<Func<Person, bool>> predicateEx) {
        var listParam = Expression.Parameter(typeof(IEnumerable<Person>), "list");
        var select = typeof(Enumerable).GetMethods().Single(m => m.Name.Equals("Where") && m.GetParameters()[1].ParameterType.GetGenericArguments().Length == 2);
        var genericMethod = select.MakeGenericMethod(new[] { typeof(Person) });
        var call = Expression.Call(null, genericMethod, new Expression[] { listParam, predicateEx });
        var lambda = Expression.Lambda<Func<IEnumerable<Person>, IEnumerable<Person>>>(call, new[] { listParam });

        return lambda.Compile().Invoke(list);
    }

如果您有一个跨越多行的非常复杂的谓词(可以从单行 lambda 计算表达式树),您可以使用一个技巧从谓词 Func 中构造一个表达式树,如下所示:

    static void Main(string[] args) {
        var strToCheck = "Address";
        Func<Person, bool> predicate = p => {
            return p.Address.Contains(strToCheck);
        };

        var list = new List<Person> { new Person { Address = "Address1" }, new Person { Address = "AAAAAA" } };
        var adresses = FilterByAddress(list, predicate);
    }

    public static IEnumerable<Person> FilterByAddress(List<Person> list, Func<Person, bool> predicate) {
        var listParam = Expression.Parameter(typeof(IEnumerable<Person>), "list");
        Expression<Func<Person, bool>> predicateEx = p => predicate(p);
        var select = typeof(Enumerable).GetMethods().Single(m => m.Name.Equals("Where") && m.GetParameters()[1].ParameterType.GetGenericArguments().Length == 2);
        var genericMethod = select.MakeGenericMethod(new[] { typeof(Person) });
        var call = Expression.Call(null, genericMethod, new Expression[] { listParam, predicateEx });
        var lambda = Expression.Lambda<Func<IEnumerable<Person>, IEnumerable<Person>>>(call, new[] { listParam });

        return lambda.Compile().Invoke(list);
    }

使用泛型方法按谓词过滤列表

    static void Main(string[] args) {
        var strToCheck = "Address";
        Func<Person, bool> predicate = p => {
            return p.Address.Contains(strToCheck);
        };

        var list = new List<Person> { new Person { Address = "Address1" }, new Person { Address = "AAAAAA" } };
        var adresses = FilterBy<Person>(list, predicate);
    }

    public static IEnumerable<T> FilterBy<T>(List<T> list, Func<T, bool> predicate) {
        var listParam = Expression.Parameter(typeof(IEnumerable<T>), "list");
        Expression<Func<T, bool>> predicateEx = p => predicate(p);
        var select = typeof(Enumerable).GetMethods().Single(m => m.Name.Equals("Where") && m.GetParameters()[1].ParameterType.GetGenericArguments().Length == 2);
        var genericMethod = select.MakeGenericMethod(new[] { typeof(T) });
        var call = Expression.Call(null, genericMethod, new Expression[] { listParam, predicateEx });
        var lambda = Expression.Lambda<Func<IEnumerable<T>, IEnumerable<T>>>(call, new[] { listParam });

        return lambda.Compile().Invoke(list);
    }
}

【讨论】:

  • 您称其为简单解决方案?对所用代码的一些解释会很好。
  • 谢谢,我会测试一下。请考虑@Flater 评论
  • 这一行:Expression&lt;Func&lt;Person, bool&gt;&gt; contains = a =&gt; a.Address.Contains(strToCheck); 我想用参数实现这段代码。我无法访问Address 属性,因为我使用了通用抽象类,而我的实体只是T
  • 另一个考虑因素是我想将Expression&lt;Func&lt;Person, bool&gt;&gt; 传递给在数据库级别过滤数据的函数。
猜你喜欢
  • 1970-01-01
  • 2012-01-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-08
  • 1970-01-01
相关资源
最近更新 更多