为什么要使用Expression <Func <T >>而不是Func <T>?

我了解lambda和FuncAction代表。 但表情让我感到难过。 在什么情况下你会使用Expression<Func<T>>而不是普通的旧Func<T>


当你想把lambda表达式当作表达式树来看待它们而不是执行它们。 例如,LINQ to SQL获取表达式并将其转换为等效的SQL语句并将其提交给服务器(而不是执行lambda)。

从概念上讲, Expression<Func<T>>Func<T>完全不同。 Func<T>表示一个delegate ,它几乎是指向一个方法的指针,而Expression<Func<T>>表示一个lambda表达式的树数据结构。 这个树结构描述了lambda表达式的作用,而不是做实际的事情。 它基本上保存有关表达式,变量,方法调用......的组成的数据(例如,它保存诸如此lambda的信息是某个常量+某个参数)。 您可以使用此描述将其转换为实际方法(使用Expression.Compile )或使用其他东西(如LINQ to SQL示例)。 将lambda作为匿名方法和表达式树处理的行为纯粹是编译时的事情。

Func<int> myFunc = () => 10; // similar to: int myAnonMethod() { return 10; }

将有效地编译为IL方法,该方法什么也得不到并返回10。

Expression<Func<int>> myExpression = () => 10;

将被转换为一个数据结构,该数据结构描述一个不带参数并返回值10的表达式:

Expression vs Func 更大的图像

虽然它们在编译时看起来相同,但编译器产生的却完全不同


我添加了一个答案为noobs,因为这些答案似乎在我的头上,直到我意识到它有多简单。 有时候会出现这样的复杂情况,那就是你无法“围绕它环抱”。

直到我陷入一个非常烦人的'错误',试图一般性地使用LINQ到SQL时,我不需要了解其中的差异:

public IEnumerable<T> Get(Func<T, bool> conditionLambda){
  using(var db = new DbContext()){
    return db.Set<T>.Where(conditionLambda);
  }
}

这很有效,直到我开始在较大的数据集上发生OutofMemoryException。 在lambda内部设置断点让我意识到,它遍历我的表中的每一行,逐个寻找匹配到我的lambda条件。 这难倒了我一段时间,因为为什么它会把我的数据表当作一个巨大的IEnumerable对待,而不是像它应该的那样对待LINQ-to-SQL? 在我的LINQ到MongoDb版本中,它也完成了同样的事情。

解决的办法是简单地将Func<T, bool>变成Expression<Func<T, bool>> ,所以我搜索了为什么它需要一个Expression而不是Func ,最后在这里结束。

表达式只是简单地将委托变成关于它自己的数据。 所以a => a + 1变成“在左边有一个int a ,在右边你给它加1”。 而已。 你现在可以回家了。 它显然比这更结构化,但这基本上都是一个表达树 - 没有任何东西可以包裹你的头。

了解这一点,很明显为什么LINQ-to-SQL需要一个Expression ,而Func是不够的。 Func没有提供一种进入自己的方式,以查看如何将其转换为SQL / MongoDb /其他查询的细节。 你不能看到它是在减法上做加法还是乘法。 你所能做的就是运行它。 另一方面, Expression允许您查看委托内部并查看其想要执行的任何操作,从而使您能够将其转换为任何想要的内容,如SQL查询。 Func没有工作,因为我的DbContext对于实际在lambda表达式中将其转换为SQL的内容是盲目的,所以它做了下一个最好的事情,并通过我的表中的每一行迭代该条件。

编辑:在约翰·彼得的要求上阐述我的最后一句话:

IQueryable扩展IEnumerable,因此IEnumerable的方法(如Where()获取接受Expression重载。 当你将一个Expression传递给它时,你保留一个IQueryable作为结果,但是当你传递一个Func ,你将返回基本的IEnumerable,并且你将得到一个IEnumerable结果。 换句话说,没有注意到你已经把你的数据集变成了一个迭代列表,而不是要查询的东西。 直到你真正看到签名背后,很难发现有什么不同。


选择Expression vs Func的一个非常重要的考虑因素是像LINQ to Entities这样的IQueryable提供者可以“消化”你在Expression中传递的内容,但是会忽略你在Func中传递的内容。 我有两篇关于这个主题的博客文章:

更多关于表达式与Func与实体框架并且爱上LINQ - 第7部分:表达式和函数(最后一节)

链接地址: http://www.djcxy.com/p/5403.html

上一篇: Why would you use Expression<Func<T>> rather than Func<T>?

下一篇: Why is quicksort better than mergesort?