使用具有IEnumerable的Lambda表达式树

我一直在努力学习更多关于使用Lamba表达式树的知识,因此我创建了一个简单的例子。 这是代码,如果作为C#程序粘贴,这可以在LINQPad中使用。

void Main() { IEnumerable list = GetUsers().Where(NameContains("a")); list.Dump("Users"); } // Methods public IEnumerable GetUsers() { yield return new User{Name = "andrew"}; yield return new User{Name = "rob"}; yield return new User{Name = "chris"}; yield return new User{Name = "ryan"}; } public Expression<Func> NameContains(string namePart) { return u => u.Name.Contains(namePart); } // Classes public class User { public string Name { get; set; } } 

这会导致以下错误:

无法从用法中推断出方法’System.Linq.Enumerable.Where(System.Collections.Generic.IEnumerable,System.Func)’的类型参数。 尝试显式指定类型参数。

但是,如果我用main替​​换main中的第一行:

 IEnumerable list = GetUsers().Where(u => u.Name.Contains("a")); 

它工作正常。 可以告诉我,我做错了吗?

Enumerable.Where方法采用Func ,而不是Expression> 。 也许你对Queryable.Where感到困惑,它确实将表达式作为参数…在你的情况下你不需要表达式,你只需要一个可以对序列中的每个项执行的委托。 表达式的目的(大部分)被分析并转换为其他东西(例如SQL),以对外部数据源执行查询

NameContains的返回类型从Expression>更改为Func 。 在这种情况下,不需要返回Expression,您实际上想要返回已编译的委托。 构成lambda的表达式与lambda(它是委托)本身之间存在差异。

如果将lambda发送到方法中,则该方法可以将lambda接受为表达式,也可以接受为已编译的委托类型,具体取决于您在参数中指定的内容。 如果传入的参数类型是表达式,则可以发送看似委托的内容,但是,如果方法需要委托,则必须为其提供已编译的委托,而不仅仅是表达式。 话虽这么说,你也可以这样做:

  var certainUsers = GetUsers().Where(NameContains("a").Compile()); 

哪个会编译表达式,并返回一个Func

Lambda表达式可以被视为代码(委托)或数据(表达式树)

在您的示例中,您尝试将lambda表达式视为代码。

如果要将lambda表达式视为数据,可以使用Expression <>声明。

你为什么想做这个?

以下是Book Linq In Action的引用,

“表达式树可以在运行时提供给工具,它们使用它们来指导它们的执行或将它们转换成其他东西,例如LINQ to SQL中的SQL。”

使用表达式树允许您获取lambda表达式并将其转换为数据,这就是Linq to SQL的工作方式,它采用lambda表达式或查询运算符或查询表达式并将它们转换为SQL。 您当然可以在转换为sql后查看和修改创建的表达式树。

Expression和Func <...>之间存在巨大差异,Func是一个可以直接调用它的纯委托,表达式是一个数据结构,包含有关lambda表达式或Linq语法的信息的表达式信息(例如来自x在列表中,x.Id = 1选择x)。 表达式不能直接调用它必须先编译,表达式用于将表达式从一种方式转换为另一种方式,如Link To Sql,它将表达式转换为Sql语句,这是改变NameContains返回类型的最佳方法使用Linq To Objects时使用表达式的方法,但是当使用Linq To Sql时,可以同时使用Expression或func。