C# 为什么要使用 Expression<Func<T>> 而不是 Func<T>?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/793571/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why would you use Expression<Func<T>> rather than Func<T>?
提问by Richard Nagle
I understand lambdas and the Func
and Action
delegates. But expressions
stump me.
我理解 lambdasFunc
和Action
代表。但是表情难倒了我。
In what circumstances would you use an Expression<Func<T>>
rather than a plain old Func<T>
?
在什么情况下您会使用 anExpression<Func<T>>
而不是普通的 old Func<T>
?
采纳答案by Mehrdad Afshari
When you want to treat lambda expressions as expression trees and look inside them instead of executing them. For example, LINQ to SQL gets the expression and converts it to the equivalent SQL statement and submits it to server (rather than executing the lambda).
当您想将 lambda 表达式视为表达式树并查看它们内部而不是执行它们时。例如,LINQ to SQL 获取表达式并将其转换为等效的 SQL 语句并将其提交给服务器(而不是执行 lambda)。
Conceptually, Expression<Func<T>>
is completely differentfrom Func<T>
. Func<T>
denotes a delegate
which is pretty much a pointer to a method and Expression<Func<T>>
denotes a tree data structurefor a lambda expression. This tree structure describes what a lambda expression doesrather than doing the actual thing. It basically holds data about the composition of expressions, variables, method calls, ... (for example it holds information such as this lambda is some constant + some parameter). You can use this description to convert it to an actual method (with Expression.Compile
) or do other stuff (like the LINQ to SQL example) with it. The act of treating lambdas as anonymous methods and expression trees is purely a compile time thing.
从概念上讲,Expression<Func<T>>
是完全不同的Func<T>
。Func<T>
表示 a delegate
,它几乎是一个指向方的指针,并Expression<Func<T>>
表示lambda 表达式的树数据结构。这种树结构描述了 lambda 表达式的作用,而不是做实际的事情。它基本上保存有关表达式、变量、方调用等组成的数据(例如,它保存诸如此 lambda 是某个常量 + 某个参数之类的信息)。您可以使用此描述将其转换为实际方(使用Expression.Compile
)或使用它执行其他操作(如 LINQ to SQL 示例)。将 lambda 视为匿名方和表达式树的行为纯粹是编译时的事情。
Func<int> myFunc = () => 10; // similar to: int myAnonMethod() { return 10; }
will effectively compile to an IL method that gets nothing and returns 10.
将有效地编译为一个什么也得不到并返回 10 的 IL 方。
Expression<Func<int>> myExpression = () => 10;
will be converted to a data structure that describes an expression that gets no parameters and returns the value 10:
将被转换为一个数据结构,该数据结构描述一个没有参数并返回值 10 的表达式:
While they both look the same at compile time, what the compiler generates is totally different.
虽然它们在编译时看起来相同,但编译器生成的内容完全不同。
回答by Andrew Hare
You would use an expression when you want to treat your function as data and not as code. You can do this if you want to manipulate the code (as data). Most of the time if you don't see a need for expressions then you probably don't need to use one.
当您想将函数视为数据而不是代码时,可以使用表达式。如果您想操作代码(作为数据),您可以这样做。大多数时候,如果您认为不需要表达式,那么您可能不需要使用表达式。
回答by Marc Gravell
LINQ is the canonical example (for example, talking to a database), but in truth, any time you care more about expressing whatto do, rather than actually doing it. For example, I use this approach in the RPC stack of protobuf-net(to avoid code-generation etc) - so you call a method with:
LINQ 是典型示例(例如,与数据库对话),但实际上,任何时候您都更关心表达要做什么,而不是实际做。例如,我在protobuf-net的 RPC 堆栈中使用这种方(以避免代码生成等) - 所以你调用一个方:
string result = client.Invoke(svc => svc.SomeMethod(arg1, arg2, ...));
This deconstructs the expression tree to resolve SomeMethod
(and the value of each argument), performs the RPC call, updates any ref
/out
args, and returns the result from the remote call. This is only possible via the expression tree. I cover this more here.
这将解构要解析的表达式树SomeMethod
(以及每个参数的值),执行 RPC 调用,更新任何ref
/ out
args,并返回远程调用的结果。这只能通过表达式树实现。我在这里详细介绍了这一点。
Another example is when you are building the expression trees manually for the purpose of compiling to a lambda, as done by the generic operatorscode.
另一个示例是当您手动构建表达式树以编译为 lambda 时,如泛型运算符代码所做的那样。
回答by LSpencer777
An extremely important consideration in the choice of Expression vs Func is that IQueryable providers like LINQ to Entities can 'digest' what you pass in an Expression, but will ignore what you pass in a Func. I have two blog posts on the subject:
在选择 Expression vs Func 时,一个极其重要的考虑因素是像 LINQ to Entities 这样的 IQueryable 提供程序可以“消化”您在 Expression 中传递的内容,但会忽略您在 Func 中传递的内容。我有两篇关于这个主题的博客文章:
More on Expression vs Func with Entity Frameworkand Falling in Love with LINQ - Part 7: Expressions and Funcs(the last section)
更多关于 Expression vs Func with Entity Framework和 Falling in Love with LINQ - Part 7: Expressions and Funcs(最后一部分)
回答by Olexander Ivanitskyi
I'd like to add some notes about the differences between Func<T>
and Expression<Func<T>>
:
我想补充约之间的差异的一些注意事项Func<T>
和Expression<Func<T>>
:
Func<T>
is just a normal old-school MulticastDelegate;Expression<Func<T>>
is a representation of lambda expression in form of expression tree;- expression tree can be constructed through lambda expression syntax or through the API syntax;
- expression tree can be compiled to a delegate
Func<T>
; - the inverse conversion is theoretically possible, but it's a kind of decompiling, there is no builtin functionality for that as it's not a straightforward process;
- expression tree can be observed/translated/modified through the
ExpressionVisitor
; - the extension methods for IEnumerable operate with
Func<T>
; - the extension methods for IQueryable operate with
Expression<Func<T>>
.
Func<T>
只是一个普通的老式 MulticastDelegate;Expression<Func<T>>
是以表达式树的形式表示的 lambda 表达式;- 表达式树可以通过 lambda 表达式语或 API 语构建;
- 表达式树可以编译为委托
Func<T>
; - 逆转换在理论上是可能的,但它是一种反编译,没有内置功能,因为它不是一个简单的过程;
- 可以通过
ExpressionVisitor
;观察/翻译/修改表达式树; - IEnumerable 的扩展方使用
Func<T>
; - IQueryable 的扩展方使用
Expression<Func<T>>
.
There's an article which describes the details with code samples:
LINQ: Func<T> vs. Expression<Func<T>>.
有一篇文章描述了代码示例的详细信息:
LINQ: Func<T> vs. Expression<Func<T>>。
Hope it will be helpful.
希望它会有所帮助。
回答by Luaan
The primary reason is when you don't want to run the code directly, but rather, want to inspect it. This can be for any number of reasons:
主要原因是当您不想直接运行代码,而是想检查它时。这可能有多种原因:
- Mapping the code to a different environment (ie. C# code to SQL in Entity Framework)
- Replacing parts of the code in runtime (dynamic programming or even plain DRY techniques)
- Code validation (very useful when emulating scripting or when doing analysis)
- Serialization - expressions can be serialized rather easily and safely, delegates can't
- Strongly-typed safety on things that aren't inherently strongly-typed, and exploiting compiler checks even though you're doing dynamic calls in runtime (ASP.NET MVC 5 with Razor is a nice example)
- 将代码映射到不同的环境(即 C# 代码到实体框架中的 SQL)
- 在运行时替换部分代码(动态编程甚至简单的 DRY 技术)
- 代码验证(在模拟脚本或进行分析时非常有用)
- 序列化 - 表达式可以相当容易和安全地序列化,委托不能
- 强类型安全对本质上不是强类型的事物,并利用编译器检查,即使您在运行时进行动态调用(ASP.NET MVC 5 with Razor 是一个很好的例子)
回答by Chad Hedgcock
I'm adding an answer-for-noobs because these answers seemed over my head, until I realized how simple it is. Sometimes it's your expectation that it's complicated that makes you unable to 'wrap your head around it'.
我正在添加一个新手的答案,因为这些答案似乎超出了我的脑海,直到我意识到它是多么简单。有时是您期望它很复杂,使您无“将头环绕”。
I didn't need to understand the difference until I walked into a really annoying 'bug' trying to use LINQ-to-SQL generically:
直到我遇到一个非常烦人的“错误”尝试一般使用 LINQ-to-SQL 之前,我才需要了解其中的区别:
public IEnumerable<T> Get(Func<T, bool> conditionLambda){
using(var db = new DbContext()){
return db.Set<T>.Where(conditionLambda);
}
}
This worked great until I started getting OutofMemoryExceptions on larger datasets. Setting breakpoints inside the lambda made me realize that it was iterating through each row in my table one-by-one looking for matches to my lambda condition. This stumped me for a while, because why the heck is it treating my data table as a giant IEnumerable instead of doing LINQ-to-SQL like it's supposed to? It was also doing the exact same thing in my LINQ-to-MongoDb counterpart.
这很有效,直到我开始在更大的数据集上获取 OutofMemoryExceptions。在 lambda 中设置断点让我意识到它正在逐行遍历表中的每一行,寻找与我的 lambda 条件匹配的内容。这让我难住了一段时间,因为为什么它将我的数据表视为一个巨大的 IEnumerable 而不是像它应该做的那样执行 LINQ-to-SQL?它也在我的 LINQ-to-MongoDb 副本中做同样的事情。
The fix was simply to turn Func<T, bool>
into Expression<Func<T, bool>>
, so I googled why it needs an Expression
instead of Func
, ending up here.
修复只是Func<T, bool>
变成Expression<Func<T, bool>>
,所以我用谷歌搜索为什么它需要一个Expression
而不是Func
,最终在这里。
An expression simply turns a delegate into a data about itself.So a => a + 1
becomes something like "On the left side there's an int a
. On the right side you add 1 to it." That's it.You can go home now. It's obviously more structured than that, but that's essentially all an expression tree really is--nothing to wrap your head around.
表达式只是将委托转换为关于自身的数据。所以a => a + 1
变成了“在左边有一个int a
。在右边你加 1”。就是这样。你现在可以回家了。它显然比那更结构化,但这基本上就是一个表达式树的全部内容——没有什么可以让你头疼的。
Understanding that, it becomes clear why LINQ-to-SQL needs an Expression
, and a Func
isn't adequate. Func
doesn't carry with it a way to get into itself, to see the nitty-gritty of how to translate it into a SQL/MongoDb/other query. You can't see whether it's doing addition or multiplication or subtraction. All you can do is run it. Expression
, on the other hand, allows you to look inside the delegate and see everything it wants to do. This empowers you to translate the delegate into whatever you want, like a SQL query. Func
didn't work because my DbContext was blind to the contents of the lambda expression. Because of this, it couldn't turn the lambda expression into SQL; however, it did the next best thing and iterated that conditional through each row in my table.
理解了这一点,就很清楚为什么 LINQ-to-SQL 需要一个Expression
,而 aFunc
是不够的。Func
没有携带进入自身的方,以了解如何将其转换为 SQL/MongoDb/其他查询的细节。你看不到它是在做加还是乘或减。你所能做的就是运行它。Expression
,另一方面,允许您查看委托内部并查看它想做的一切。这使您能够将委托转换为您想要的任何内容,例如 SQL 查询。Func
不起作用,因为我的 DbContext 对 lambda 表达式的内容视而不见。因此,它无将 lambda 表达式转换为 SQL;然而,它做了下一个最好的事情,并在我的表中的每一行中迭代该条件。
Edit: expounding on my last sentence at John Peter's request:
编辑:在约翰彼得的要求下阐述我的最后一句话:
IQueryable extends IEnumerable, so IEnumerable's methods like Where()
obtain overloads that accept Expression
. When you pass an Expression
to that, you keep an IQueryable as a result, but when you pass a Func
, you're falling back on the base IEnumerable and you'll get an IEnumerable as a result. In other words, without noticing you've turned your dataset into a list to be iterated as opposed to something to query. It's hard to notice a difference until you really look under the hood at the signatures.
IQueryable 扩展了 IEnumerable,因此 IEnumerable 的方如Where()
获取接受Expression
. 当您将 an 传递Expression
给它时,您将保留一个 IQueryable 作为结果,但是当您传递一个 时Func
,您将退回到基础 IEnumerable 并且您将获得一个 IEnumerable 作为结果。换句话说,您没有注意到您已经将数据集变成了要迭代的列表,而不是要查询的列表。除非您真正深入了解签名,否则很难注意到差异。
回答by O?uzhan Soykan
There is a more philosophical explanation about it from Krzysztof Cwalina's book(Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries);
Krzysztof Cwalina 的书(框架设计指南:可重用 .NET 库的约定、习语和模式)中有一个更哲学的解释;
Edit for non-image version:
编辑非图像版本:
Most times you're going to want Funcor Actionif all that needs to happen is to run some code. You need Expressionwhen the code needs to be analyzed, serialized, or optimized before it is run. Expressionis for thinking about code, Func/Actionis for running it.
大多数情况下,如果只需要运行一些代码,您将需要Func或Action。当代码在运行前需要分析、序列化或优化时,您需要Expression。Expression用于思考代码,Func/Action用于运行它。
回答by mhenry1384
I don't see any answers yet that mention performance. Passing Func<>
s into Where()
or Count()
is bad. Real bad. If you use a Func<>
then it calls the IEnumerable
LINQ stuff instead of IQueryable
, which means that whole tables get pulled in and thenfiltered. Expression<Func<>>
is significantly faster, especially if you are querying a database that lives another server.
我还没有看到任何提到性能的答案。将Func<>
s 传入Where()
orCount()
是不好的。真糟糕。如果你使用 aFunc<>
那么它调用IEnumerable
LINQ 的东西而不是IQueryable
,这意味着整个表被拉入然后过滤。 Expression<Func<>>
明显更快,尤其是当您查询位于另一台服务器上的数据库时。