【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>
我了解lambda和Func
和Action
代表。 但是表情让我难过。 在什么情况下,您将使用Expression<Func<T>>
而不是普通的旧Func<T>
?
#1楼
我想添加一些关于Func<T>
和Expression<Func<T>>
之间的区别的注释:
-
Func<T>
只是一个普通的老式MulticastDelegate; -
Expression<Func<T>>
以表达式树的形式表示lambda表达式; - 表达式树可以通过lambda表达式语法或API语法构造;
- 表达式树可以编译为委托
Func<T>
; - 从理论上讲,逆转换是可能的,但是这是一种反编译,因为它不是一个简单的过程,所以没有内置功能;
- 可以通过
ExpressionVisitor
观察/翻译/修改表达树; - IEnumerable的扩展方法与
Func<T>
; - IQueryable的扩展方法使用
Expression<Func<T>>
。
有一篇文章描述了代码示例的详细信息:
LINQ:Func <T>与Expression <Func <T >> 。
希望对您有所帮助。
#2楼
主要原因是当您不想直接运行代码,而是想对其进行检查时。 这可能有多种原因:
- 将代码映射到其他环境(即,将C#代码映射到Entity Framework中的SQL)
- 在运行时替换部分代码(动态编程甚至普通的DRY技术)
- 代码验证(在仿真脚本或进行分析时非常有用)
- 序列化-表达式可以很容易且安全地进行序列化,委托不能
- 在本质上不是强类型的事物上具有强类型的安全性,即使您在运行时中进行动态调用,也可以利用编译器检查(带有Razor的ASP.NET MVC 5是一个很好的示例)
#3楼
我添加了一个新手答案,因为这些答案似乎在我头上,直到我意识到它是如此简单。 有时,您期望它很复杂,使您无法“绕头绕”。
在走进一个非常烦人的“ bug”尝试通用使用LINQ-to-SQL之前,我不需要了解它们之间的区别:
public IEnumerable<T> Get(Func<T, bool> conditionLambda){
using(var db = new DbContext()){
return db.Set<T>.Where(conditionLambda);
}
}
直到我开始在更大的数据集上获取OutofMemoryExceptions为止,这一直很好。 在lambda内设置断点使我意识到它正在一张一张地遍历表中的每一行,以寻找与我的lambda条件匹配的对象。 这让我难过了一阵子,因为为什么要把我的数据表当作一个巨大的IEnumerable而不是像预期的那样进行LINQ-to-SQL呢? 在我的LINQ-to-MongoDb对应程序中,它也在做完全相同的事情。
解决方法只是将Func<T, bool>
转换为Expression<Func<T, bool>>
,所以我用Google搜索了为什么它需要一个Expression
而不是Func
,到这里结束。
表达式只是将委托转换为有关其自身的数据。 因此, a => a + 1
类似于“在左侧有一个int a
。在右侧您向其添加1”。 而已。 你现在可以回家了。 它显然比这更结构化,但是本质上,这实际上是一个表达式树的全部内容,无所不用其极。
理解了这一点,就清楚了为什么LINQ-to-SQL为什么需要Expression
,而Func
不足。 Func
并没有提供一种进入自身的方法,来了解如何将其转换为SQL / MongoDb /其他查询的精髓。 您看不到它是在做加法,乘法还是减法。 您所能做的就是运行它。 另一方面, Expression
使您可以查看委托内部,并查看其要执行的所有操作。 这使您能够将委托转换为所需的任何内容,例如SQL查询。 Func
不起作用,因为我的DbContext对lambda表达式的内容视而不见。 因此,它无法将lambda表达式转换为SQL。 但是,它做的第二件事是最好的,并遍历表中的每一行。
编辑:应约翰·彼得的要求,对我的最后一句话进行解释:
IQueryable扩展了IEnumerable,因此IEnumerable的方法(如Where()
获得接受Expression
重载。 将Expression
传递给该Expression
时,结果将保留一个IQueryable,而传递一个Func
,您将回到基本的IEnumerable上,从而得到一个IEnumerable。 换句话说,没有注意到您已将数据集变成要迭代的列表,而不是要查询的列表。 除非您真正看一下签名,否则很难注意到差异。
#4楼
Krzysztof Cwalina的书(《 框架设计指南:可重用的.NET库的约定,惯用语和模式》)对此有一个更哲学的解释。
编辑非图像版本:
大多数情况下,如果只需要运行一些代码,便会需要Func或Action 。 当需要在运行之前对代码进行分析,序列化或优化时,需要使用Expression 。 表达式用于考虑代码, Func / Action用于运行代码。
#5楼
我还没有任何关于性能的答案。 将Func<>
传递到Where()
或Count()
是不好的。 真糟糕。 如果使用Func<>
它将调用IEnumerable
LINQ而不是IQueryable
,这意味着将整个表放入并进行过滤。 Expression<Func<>>
显着更快,尤其是在查询位于另一台服务器上的数据库时。
来源:oschina
链接:https://my.oschina.net/stackoom/blog/3143327