你为什么要使用表达 <Func<T> >而不是Func <T> ?

Deadly 提交于 2019-12-15 21:11:06

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

我了解lambda和FuncAction代表。 但是表情让我难过。 在什么情况下,您将使用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库的约定,惯用语和模式》)对此有一个更哲学的解释。

编辑非图像版本:

大多数情况下,如果只需要运行一些代码,便会需要FuncAction 当需要在运行之前对代码进行分析,序列化或优化时,需要使用Expression 表达式用于考虑代码, Func / Action用于运行代码。


#5楼

我还没有任何关于性能的答案。 将Func<>传递到Where()Count()是不好的。 真糟糕。 如果使用Func<>它将调用IEnumerable LINQ而不是IQueryable ,这意味着将整个表放入并进行过滤。 Expression<Func<>>显着更快,尤其是在查询位于另一台服务器上的数据库时。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!