Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

linq "let" translation

Tags:

c#

linq

I understand that when the C# compiler sees a linq query comprehension, it basically does a straight translation to the corresponding Linq Extension methods and lambdas. i.e.

from x in list select x.property 

gets translated to:

list.Select(x => x.property) 

my question is what do let clauses get translated to. for example how would this get translated by the compiler.

from x in list let v = SomeComplexExpressionDependingOnx select v 

(p.s. i know this could be reduced to just select SomeComplexExpressionDependingOnx but i want to know how this is done in general)

Thanks!

like image 665
luke Avatar asked Jun 09 '10 01:06

luke


People also ask

What is let in LINQ?

The Let keyword allows you to create a range variable and initialized with the result of the query expression and then you are allowed to use that variable with the upcoming clause in the same query.

What is let keyword in C#?

In this article You can do this with the let keyword, which creates a new range variable and initializes it with the result of the expression you supply. Once initialized with a value, the range variable cannot be used to store another value. However, if the range variable holds a queryable type, it can be queried.

Does C# have let?

C# let KeywordUse the let keyword in query expressions to declare and assign variables that can be reused. Let. This C# keyword is a part of a query expression. It introduces a variable.

How do you use SkipWhile?

SkipWhile(n => n < 9); In this example the SkipWhile operator will skip the items from the starting position until the conditions fails. Once the condition has failed then it will return the remaining items as an IEnumerable collection. So the output for this example is 9, 8, 6 and 7.


2 Answers

In this particular case, it gets translated to:

list.Select( x => SomeComplexExpressionDependingOnx ); 

But there may be a more complex case, such as:

from x in list let v = SomeComplexExpressionDependingOnx where v > 10 && v+5 < 50 && SomeFunc(v) == "str" select x 

Will translate to:

list.Where( x =>      {         var v = SomeComplexExpressionDependingOnx;         return v > 10 && v+5 < 50 && SomeFunc(v) == "str";     } ) 

In other words, the let keyword is a way to minimize and/or optimize your query. That is, without the let keyword you would have to write:

from x in list where     SomeComplexExpressionDependingOnx > 10 &&     SomeComplexExpressionDependingOnx+5 < 50 &&     SomFunc(SomeComplexExpressionDependingOnx) == "str" select x 

Resulting in possible triple evaluation of the same expression.

Update, following a question in comment.

First, what's so scary about "block expressions"? They're just a shorthand for arbitrary delegate. That is, the following expression:

Func<string,int> f =      s =>     {         var ln = s.Length;         return ln/2;     } 

Is equivalent to the following:

int CompilerGeneratedMethodIdentifier0( string s ) {     var ln = s.Length;     return ln/2; }  ...  Func<string, int> f = new Func<string, int>( CompilerGeneratedMethodIdentifier0 ); 

Second, what's so special about "block expressions"? Did you know that mmm... let's call them "non-block" expressions also expand to the very same code? That is, the simple code new Func<string,int>( s => s.Length/2 ) is absolute equivalent to:

int CompilerGeneratedMethodIdentifier0( string s ) {     return s.Length/2; }  ...  new Func<string, int>( CompilerGeneratedMethodIdentifier0 ); 

Third, what's so non-linqy about "block expressions"? LINQ uses delegates all over the place, and it doesn't really matter to LINQ what exact shortcut you use to represent those delegates.

In particular, your expression from a in list where a.SomeProp > 10 select new { A = a, B = a.GetB() } gets translated into the following:

class AnonymousType0 {     public MyClass A { get; set; }     public othertype B { get; set; } }  bool WhereFunc0( MyClass a ) {     return a.SomeProp > 10; }  AnonymousType0 SelectResultFunc0( MyClass a ) {     AnonymousType0 result = new AnonymousType0();     result.A = a;     result.B = a.GetB();     return result; }  ...  list     .Where( new Func<MyClass,bool>( WhereFunc0 ) )     .Select( new Func<MyClass,AnonymousType0>( SelectResultFunc0 ) ); 

Fourth, to get understanding like this, one can just play with the language and explore.

And fifth, if the previous advice doesn't work for you for one reason or another, you always have ILSpy. Very useful tool, everybody should have one.

like image 140
Fyodor Soikin Avatar answered Sep 28 '22 08:09

Fyodor Soikin


Take a look at LINQPad, you can write the query and hit the lamba symbol to see what the output will look like. For example I took this query:

var names = new[] { "Tom", "Dick", "Harry", "Mary", "Jay" }.AsQueryable();  var results =      from n in names     let n1 = String.IsNullOrEmpty(n)     select n1;  results.Dump(); 

And it output the following:

System.String[]    .Select (       n =>           new            {             n = n,              n1 = String.IsNullOrEmpty (n)          }    )    .Select (temp0 => temp0.n1) 

So it does indeed look like the let is translated to a temp value as anonymous, and then consumed in the outer select statement.

I love LINQPad for the ability to write the query and see how it would translate.

like image 33
Jason Short Avatar answered Sep 28 '22 08:09

Jason Short