简体   繁体   English

用于Enumerable和Queryable扩展方法的Lambda表达式参数

[英]Lambda expression arguments for Enumerable and Queryable extension methods

A lambda expression is an anonymous method, which under the covers is a delegate so I can do something like this: Lambda表达式是一个匿名方法,在后台它是一个委托,因此我可以执行以下操作:

 delegate bool Foo(int x);

 Foo bar = x => x == 1;

Passing this delegate to an Enumerable extension method makes perfect sense, as the typical expected argument is a Func , which is shorthand for a delegate: 将此委托传递给Enumerable扩展方法是很有意义的,因为典型的期望参数是Func ,它是委托的简写:

 public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);

However, I am unclear about how it is possible to pass in the delegate to a Queryable extension method like this one: 但是,我不清楚如何将委托传递给像这样的Queryable扩展方法:

 public static IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);

This method expects an Expression<TDelegate> argument, but it is perfectly legal to pass in a lambda expression. 此方法需要一个Expression<TDelegate>参数,但是传入lambda表达式是完全合法的。 What is the mechanism that coerces the lambda expression into Expression<TDelegate> so that it may be consumed? 将lambda表达式Expression<TDelegate>转换为Expression<TDelegate>以便可以被使用的机制是什么?

I am familiar with the fact that Queryable methods build out expression trees for parsing by providers, I'm just curious about this one aspect that isn't immediately obvious to me. 我对Queryable方法建立了表达式树以供提供程序进行解析的事实非常熟悉,我只是对这一方面感到好奇,这一方面对我而言并不立即显而易见。

UPDATE 更新

I'm becoming less ignorant about my ignorance. 我对我的无知越来越不了解。 Lambda expressions aren't delegates, but can be used to create either delegates or expressions: Lambda表达式不是委托,但可用于创建委托或表达式:

 Expression<Func<int, bool>> foo = c => c == 1;

Does the compiler infer the type based on the context? 编译器是否根据上下文推断类型? I'm guessing that must be the case, as this isn't legal: 我猜测一定是这样,因为这是不合法的:

var foo = c => c == 1;

This is described in the specification: 在规范中对此进行了描述:

4.6 Expression Tree Types 4.6表达式树类型

If a conversion exists from a lambda expression to a delegate type D , a conversion also exists to the expression tree type Expression<D> . 如果存在从lambda表达式到委托类型D的转换,则也存在到表达式树类型Expression<D> Whereas the conversion of a lambda expression to a delegate type generates a delegate that references executable code for the lambda expression, conversion to an expression tree type creates an expression tree representation of the lambda expression. 将lambda表达式转换为委托类型会生成引用了lambda表达式的可执行代码的委托,而转换为表达式树类型则会创建lambda表达式的表达式树表示形式。 Expression trees are efficient in-memory data representations of lambda expressionsand make the structure of the lambda expressiontransparent and explicit 表达式树是lambda表达式的有效内存数据表示形式,并使lambda表达式的结构透明且显式

So there is a conversion from a lambda to a compatible expression tree type, and the compiler emits the equivalent expression tree instead of creating a delegate. 因此,存在从lambda到兼容的表达式树类型的转换,并且编译器发出等效的表达式树而不是创建委托。

Quite simply you can't. 很简单,您做不到。 However, to make IQueryable methods useful, VS2008 and above include a clever compiler trick. 但是,为了使IQueryable方法有用,VS2008及更高版本包含了一个聪明的编译器技巧。 That a lambda expression that is a single statement may be assignable to both a delegate , and an Expression<TDelegate> . 可以将作为单个语句的lambda expression分配给delegateExpression<TDelegate> The compiler normally will hoist the expression and make a method. 编译器通常会提升表达式并创建一个方法。

But for an assignment to an Expression<TDelegate> it breaks down the statements into their syntactic meaning and turns that into an expression tree. 但是对于一个Expression<TDelegate>的赋值,它将语句分解为它们的语法含义,并将其转变为一个表达式树。

eg 例如

Func<int,int> func = x=>x*x;
Expression<Func<int,int>> expression = x=>x*x;

The first one will probably be turned into a static method with a garbled name something like:: 第一个可能会变成一个带有乱码名称的静态方法,例如:

private static int <B012>SomeMethod(int x){
   return x*x;
}

Where as the second statement will be transformed into something like:: 其中,第二条语句将转换为:

ParameterExpression paramX = Expression.Parameter(typeof(int));
Expression<Func<int,int>> expression = Expression.Lambda<Func<int,int>>(
     Expression.Multiply(paramX,paramX),paramX);

But you can not do:: 但是你不能做:

expression = func;

This is not valid, as func is a delegate . 这是无效的,因为funcdelegate You can do this though:: 您可以执行以下操作:

func=expression.Compile()

Which compiles the expressions into a func. 将表达式编译成函子。

**Note the suggested transformations may not be a 100% correct. **请注意,建议的转换可能不是100%正确的。

The reason they did this was to allow LINQ-to-Objects (Basically Map/Reduce from other language) to share the same friendly syntax as LINQ-To-Providers. 他们这样做的原因是允许LINQ-to-Objects(基本上是其他语言的Map / Reduce)共享与LINQ-To-Providers相同的友好语法。 So you can write a statement that means the same thing but can change where the filtering and transformation happens. 因此,您可以编写一条具有相同含义的语句,但可以更改过滤和转换发生的位置。 GetEmployees().Where(e=>e.LastName=="Smith") Can read the same, but could in theory be describing doing the filtering on this box, or an the database, or parsing an xml file or any number of various things. GetEmployees().Where(e=>e.LastName=="Smith")可以读取相同内容,但从理论上讲可以描述在此框或数据库上进行过滤,或者解析xml文件或任意数量的各种各样的东西。

I believe this has to do with how the query is built on an IQueryable. 我认为这与如何在IQueryable上构建查询有关。 The method requires an expression tree because it can be looked inside and structured to match a more optimized (potentially) query or map more closely to the underlying data source. 该方法需要一个表达式树,因为它可以在内部查看并进行结构化,以匹配更优化(可能)的查询或更紧密地映射到基础数据源。 So simply passing in a Func would allow only for execution, whereas an Expression < Func > allows for expression trees which can be observed and executed. 因此,简单地传递一个Func仅允许执行,而Expression < Func >允许可以观察和执行的表达式树。

Also, probably more closely answering your exact question, check this SO post out . 另外,可能更接近回答您的确切问题, 请查看此SO发布

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM