I understand lambdas and the Func
and Action
delegates. But expressions stump me. In what circumstances would you use an Expression<Func<T>>
rather than a plain old Func<T>
?
我理解lambdas和Func和Action代表。但我表情树桩。在什么情况下,你会使用一个表达式
9 个解决方案
#1
928
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 different from Func<T>
. Func<T>
denotes a delegate
which is pretty much a pointer to a method and Expression<Func<T>>
denotes a tree data structure for a lambda expression. This tree structure describes what a lambda expression does rather 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.
从概念上讲,表达
Func<int> myFunc = () => 10; // similar to: int myAnonMethod() { return 10; }
will effectively compile to an IL method that gets nothing and returns 10.
将有效地编译成IL方法,该方法得不到任何结果并返回10。
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.
虽然它们在编译时看起来是相同的,但是编译器生成的是完全不同的。
#2
173
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:
我不需要理解这个区别,直到我走进一个真正令人讨厌的“bug”,试图用linqto - 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.
直到我开始在更大的数据集上获得outofmemoryexception时,这种方法才奏效。在lambda中设置断点使我意识到它正在遍历表中的每一行,查找与我的lambda条件匹配的值。这让我为难了一段时间,因为为什么它把我的数据表当成一个巨大的IEnumerable,而不是像它应该做的linqto - sql那样?在我的linqto - 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
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 on subtraction. All you can do is run it. Expression
, on the other hand, allows you to look inside the delegate and see everything it's wanting to do, empowering you to translate it into whatever you want, like a SQL query. Func
didn't work because my DbContext was blind to what was actually in the lambda expression to turn it into SQL, so it did the next best thing and iterated that conditional through each row in my table.
理解这一点,就会明白为什么linqto - sql需要一个表达式,而Func是不够的。Func并没有带着一种方法进入它自己,去查看如何将它转换成SQL/MongoDb/其他查询的细节。你看不出它是做加法还是乘减法。你所能做的就是运行它。另一方面,表达式允许您查看委托并查看它想要做的所有事情,授权您将其转换为您想要的任何内容,比如SQL查询。Func不起作用,因为我的DbContext对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()中获取接受表达式的重载。当您传递一个表达式时,您可以保持一个可查询的结果,但是当您通过一个Func时,您就会返回到这个基础IEnumerable,您将得到一个IEnumerable结果。换句话说,没有注意到您已经将您的数据集变成了一个迭代列表,而不是查询。在你真正看到签名下面的引擎盖之前,你很难注意到它们之间的差别。
#3
91
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:
在选择表达式vs . Func时,一个极其重要的考虑是,像LINQ这样的可查询的提供者可以“消化”您传递的表达式,但是将忽略在Func中传递的内容。我有两篇关于这个主题的博文:
More on Expression vs Func with Entity Framework and Falling in Love with LINQ - Part 7: Expressions and Funcs (the last section)
More on Expression vs Func with Entity Framework and Falling in Love with LINQ - Part 7: Expression and Funcs(最后一节)
#4
59
I'd like to add some notes about the differences between Func<T>
and Expression<Func<T>>
:
我想补充一些关于Func
-
Func<T>
is just a normal old-school MulticastDelegate; -
Func
只是一个正常的老式的多播委托; -
Expression<Func<T>>
is a representation of lambda expression in form of expression tree; -
表达式
>是表达式树形式的lambda表达式的表示形式; - expression tree can be constructed through lambda expression syntax or through the API syntax;
- 表达式树可以通过lambda表达式语法或通过API语法构建;
- expression tree can be compiled to a delegate
Func<T>
; -
表达式树可以编译为委托Func
; - 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
; - 表达树可以通过ExpressionVisitor来观察/翻译/修改;
- the extension methods for IEnumerable operate with
Func<T>
; -
IEnumerable的扩展方法使用Func
; - the extension methods for IQueryable operate with
Expression<Func<T>>
. -
IQueryable的扩展方法使用表达式
>。
There's an article which describes the details with code samples:
LINQ: Func<T> vs. Expression<Func<T>>.
有一篇文章描述了代码示例的细节:LINQ: Func
Hope it will be helpful.
希望会有帮助。
#5
36
There is a more philosophical explanation about it from Krzysztof Cwalina's book(Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries);
从Cwalina的书(框架设计指南:用于可重用的。net库的约定、习惯用语和模式)中,有一个更哲学的解释。
Edit for non-image version:
编辑为非版本:
Most times you're going to want Func or Action if all that needs to happen is to run some code. You need Expression when the code needs to be analyzed, serialized, or optimized before it is run. Expression is for thinking about code, Func/Action is for running it.
大多数情况下,如果需要运行一些代码,就需要Func或Action。当代码需要在运行之前进行分析、序列化或优化时,您需要表达式。表达式是用来思考代码的,Func/Action是用来运行它的。
#6
33
LINQ is the canonical example (for example, talking to a database), but in truth, any time you care more about expressing what to 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 operators code.
另一个例子是,当您手动构建表达式树时,将其编译为lambda,就像一般操作符代码所做的那样。
#7
18
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.
当您希望将函数视为数据而不是代码时,您将使用表达式。如果您想要操作代码(作为数据),您可以这样做。大多数情况下,如果您不需要表达式,那么您可能不需要使用表达式。
#8
15
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)
- 将代码映射到不同的环境(ie)。c#代码到SQL的实体框架)
- Replacing parts of the code in runtime (dynamic programming or even plain DRY techniques)
- 在运行时替换部分代码(动态编程,甚至是简单的DRY技术)
- 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)
- 强类型安全的东西不是天生强类型的,并且利用编译器检查,即使你在运行时进行动态调用(ASP)。NET MVC 5与Razor是一个很好的例子)
#9
5
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 then filtered. Expression<Func<>>
is significantly faster, especially if you are querying a database that lives another server.
我看不出有什么答案。通过Func<>s到Where()或Count()是不好的。真正的坏。如果使用Func<>,那么它就调用IEnumerable LINQ,而不是IQueryable,这意味着整个表被拉进来然后过滤。表达式
#1
928
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 different from Func<T>
. Func<T>
denotes a delegate
which is pretty much a pointer to a method and Expression<Func<T>>
denotes a tree data structure for a lambda expression. This tree structure describes what a lambda expression does rather 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.
从概念上讲,表达
Func<int> myFunc = () => 10; // similar to: int myAnonMethod() { return 10; }
will effectively compile to an IL method that gets nothing and returns 10.
将有效地编译成IL方法,该方法得不到任何结果并返回10。
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.
虽然它们在编译时看起来是相同的,但是编译器生成的是完全不同的。
#2
173
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:
我不需要理解这个区别,直到我走进一个真正令人讨厌的“bug”,试图用linqto - 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.
直到我开始在更大的数据集上获得outofmemoryexception时,这种方法才奏效。在lambda中设置断点使我意识到它正在遍历表中的每一行,查找与我的lambda条件匹配的值。这让我为难了一段时间,因为为什么它把我的数据表当成一个巨大的IEnumerable,而不是像它应该做的linqto - sql那样?在我的linqto - 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
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 on subtraction. All you can do is run it. Expression
, on the other hand, allows you to look inside the delegate and see everything it's wanting to do, empowering you to translate it into whatever you want, like a SQL query. Func
didn't work because my DbContext was blind to what was actually in the lambda expression to turn it into SQL, so it did the next best thing and iterated that conditional through each row in my table.
理解这一点,就会明白为什么linqto - sql需要一个表达式,而Func是不够的。Func并没有带着一种方法进入它自己,去查看如何将它转换成SQL/MongoDb/其他查询的细节。你看不出它是做加法还是乘减法。你所能做的就是运行它。另一方面,表达式允许您查看委托并查看它想要做的所有事情,授权您将其转换为您想要的任何内容,比如SQL查询。Func不起作用,因为我的DbContext对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()中获取接受表达式的重载。当您传递一个表达式时,您可以保持一个可查询的结果,但是当您通过一个Func时,您就会返回到这个基础IEnumerable,您将得到一个IEnumerable结果。换句话说,没有注意到您已经将您的数据集变成了一个迭代列表,而不是查询。在你真正看到签名下面的引擎盖之前,你很难注意到它们之间的差别。
#3
91
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:
在选择表达式vs . Func时,一个极其重要的考虑是,像LINQ这样的可查询的提供者可以“消化”您传递的表达式,但是将忽略在Func中传递的内容。我有两篇关于这个主题的博文:
More on Expression vs Func with Entity Framework and Falling in Love with LINQ - Part 7: Expressions and Funcs (the last section)
More on Expression vs Func with Entity Framework and Falling in Love with LINQ - Part 7: Expression and Funcs(最后一节)
#4
59
I'd like to add some notes about the differences between Func<T>
and Expression<Func<T>>
:
我想补充一些关于Func
-
Func<T>
is just a normal old-school MulticastDelegate; -
Func
只是一个正常的老式的多播委托; -
Expression<Func<T>>
is a representation of lambda expression in form of expression tree; -
表达式
>是表达式树形式的lambda表达式的表示形式; - expression tree can be constructed through lambda expression syntax or through the API syntax;
- 表达式树可以通过lambda表达式语法或通过API语法构建;
- expression tree can be compiled to a delegate
Func<T>
; -
表达式树可以编译为委托Func
; - 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
; - 表达树可以通过ExpressionVisitor来观察/翻译/修改;
- the extension methods for IEnumerable operate with
Func<T>
; -
IEnumerable的扩展方法使用Func
; - the extension methods for IQueryable operate with
Expression<Func<T>>
. -
IQueryable的扩展方法使用表达式
>。
There's an article which describes the details with code samples:
LINQ: Func<T> vs. Expression<Func<T>>.
有一篇文章描述了代码示例的细节:LINQ: Func
Hope it will be helpful.
希望会有帮助。
#5
36
There is a more philosophical explanation about it from Krzysztof Cwalina's book(Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries);
从Cwalina的书(框架设计指南:用于可重用的。net库的约定、习惯用语和模式)中,有一个更哲学的解释。
Edit for non-image version:
编辑为非版本:
Most times you're going to want Func or Action if all that needs to happen is to run some code. You need Expression when the code needs to be analyzed, serialized, or optimized before it is run. Expression is for thinking about code, Func/Action is for running it.
大多数情况下,如果需要运行一些代码,就需要Func或Action。当代码需要在运行之前进行分析、序列化或优化时,您需要表达式。表达式是用来思考代码的,Func/Action是用来运行它的。
#6
33
LINQ is the canonical example (for example, talking to a database), but in truth, any time you care more about expressing what to 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 operators code.
另一个例子是,当您手动构建表达式树时,将其编译为lambda,就像一般操作符代码所做的那样。
#7
18
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.
当您希望将函数视为数据而不是代码时,您将使用表达式。如果您想要操作代码(作为数据),您可以这样做。大多数情况下,如果您不需要表达式,那么您可能不需要使用表达式。
#8
15
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)
- 将代码映射到不同的环境(ie)。c#代码到SQL的实体框架)
- Replacing parts of the code in runtime (dynamic programming or even plain DRY techniques)
- 在运行时替换部分代码(动态编程,甚至是简单的DRY技术)
- 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)
- 强类型安全的东西不是天生强类型的,并且利用编译器检查,即使你在运行时进行动态调用(ASP)。NET MVC 5与Razor是一个很好的例子)
#9
5
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 then filtered. Expression<Func<>>
is significantly faster, especially if you are querying a database that lives another server.
我看不出有什么答案。通过Func<>s到Where()或Count()是不好的。真正的坏。如果使用Func<>,那么它就调用IEnumerable LINQ,而不是IQueryable,这意味着整个表被拉进来然后过滤。表达式