我想知道在使用Expression<>
和不使用之间的区别是什么?
我经常看到在LinQ中大量使用Expression<Foo>
,但到目前为止,我还没有找到任何解释这种方式与只使用委托之间差异的文章。
例如:
Func<int, bool> Is42 = (value) => value == 42;
对比。
Expression<Func<int, bool>> Is42 = (value) => value == 42;
我想知道在使用Expression<>
和不使用之间的区别是什么?
我经常看到在LinQ中大量使用Expression<Foo>
,但到目前为止,我还没有找到任何解释这种方式与只使用委托之间差异的文章。
例如:
Func<int, bool> Is42 = (value) => value == 42;
对比。
Expression<Func<int, bool>> Is42 = (value) => value == 42;
tl;dr, 持有一个表达式就像持有一个应用程序的源代码,而委托则是运行该应用程序的可执行文件。表达式就像是将要运行的代码的“源”(即语法树)。委托是您将要运行的特定编译版本并完成操作的内容。
通过将lambda表达式存储为委托,您正在存储一个执行某个操作的特定委托实例。它无法修改,只能调用它。一旦获得委托,检查其所做的事情和其他信息的选项有限。
通过将lambda表达式存储为表达式,您正在存储代表委托的表达式树。可以对其进行操作以执行其他操作,例如更改其参数,更改主体并使其执行完全不同的操作。甚至可以将其重新编译为委托,因此如果需要,可以调用它。您可以轻松地检查表达式以查看其参数,它所做的事情以及它如何执行该操作。这是查询提供者可用于理解和将表达式转换为另一种语言(例如编写相应表达式树的SQL查询)的功能。
使用表达式动态创建委托比发出代码要容易得多。您可以将代码视为类似于编译器查看代码的表达式,而不是将代码视为低级别的IL指令。
因此,通过表达式,您能够做比简单的匿名委托更多的事情。虽然这并非免费,但是如果与常规方法或匿名委托相比运行已编译的表达式,性能会受到影响。但是,如果使用表达式的其他优点对您很重要,那么可能不会有问题。
Func<>
只是一个委托类型。Expression是操作的完整树的运行时表示形式,可以在运行时编译为委托(可选)。正是这个树被表达式解析器(例如Linq-to-SQL)解析,以生成SQL语句或执行其他聪明的操作。当您将lambda分配给Expression类型时,编译器会生成此表达式树以及通常的IL代码。关于表达式树的更多信息。
Func<int, bool> Is42 = (value) => value == 42;
Func<int, bool> Is42 = new Func<int, bool>((@value) => value == 42);
Expression<Func<int, bool>> Is42 = (value) => value == 42;
Expression<Func<int, bool>> Is42 = (value) => value == 42;
ParameterExpression[] parameterExpressionArray;
ParameterExpression parameterExpression = Expression.Parameter(typeof(int), "value");
Expression<Func<int, bool>> Is42 = Expression.Lambda<Func<int, bool>>(Expression.Equal(parameterExpression, Expression.Constant(42, typeof(int))), new ParameterExpression[] { parameterExpression });
System.Linq.Expressions.BinaryExpression
System.Linq.Expressions.BlockExpression
System.Linq.Expressions.ConditionalExpression
System.Linq.Expressions.ConstantExpression
System.Linq.Expressions.DebugInfoExpression
System.Linq.Expressions.DefaultExpression
System.Linq.Expressions.DynamicExpression
System.Linq.Expressions.GotoExpression
System.Linq.Expressions.IndexExpression
System.Linq.Expressions.InvocationExpression
System.Linq.Expressions.LabelExpression
System.Linq.Expressions.LambdaExpression
System.Linq.Expressions.ListInitExpression
System.Linq.Expressions.LoopExpression
System.Linq.Expressions.MemberExpression
System.Linq.Expressions.MemberInitExpression
System.Linq.Expressions.MethodCallExpression
System.Linq.Expressions.NewArrayExpression
System.Linq.Expressions.NewExpression
System.Linq.Expressions.ParameterExpression
System.Linq.Expressions.RuntimeVariablesExpression
System.Linq.Expressions.SwitchExpression
System.Linq.Expressions.TryExpression
System.Linq.Expressions.TypeBinaryExpression
System.Linq.Expressions.UnaryExpression
除了其他人写的(完全正确)之外,我要补充一点,通过Expression
类,您可以在运行时创建新方法。有一些限制。并不是所有您可以在C#中做的事情都可以在Expression
树中完成(至少在.NET 3.5中。随着.NET 4.0的推出,他们添加了大量可能的Expression
“类型”)。使用它的好处可能是(例如)创建动态查询并将其传递给LINQ-to-SQL或根据用户输入进行一些过滤...(如果您只想要与LINQ-to-SQL不兼容的动态方法,则始终可以使用CodeDom来实现此目的,但直接发出IL代码相当困难 :-))
IQueryable<T>
的主要构建块。由于查询是有类型的,因此需要该类型上的表达式。因此,为表达式提供类型允许更容易地实现查询提供程序,在其中可以在编译时强制执行类型。 - Jeff Mercado