Я понимаю, что, когда компилятор C# видит понимание запроса linq, он в основном делает прямой перевод в соответствующие Дополнительные методы Linq и лямбды. т.е.
from x in list
select x.property
переводится в:
list.Select(x => x.property)
мой вопрос - то, что делает let
пункты переводятся в. например, как это перевести компилятором.
from x in list
let v = SomeComplexExpressionDependingOnx
select v
(p.s. я знаю, что это могло быть уменьшено до просто select SomeComplexExpressionDependingOnx
но я хочу знать, как это сделано в целом),
Спасибо!
В данном конкретном случае он переводится в:
list.Select( x => SomeComplexExpressionDependingOnx );
Но может быть и более сложный случай, например:
from x in list
let v = SomeComplexExpressionDependingOnx
where v > 10 && v+5 < 50 && SomeFunc(v) == "str"
select x
Будет переводиться в:
list.Where( x =>
{
var v = SomeComplexExpressionDependingOnx;
return v > 10 && v+5 < 50 && SomeFunc(v) == "str";
}
)
Другими словами, ключевое слово let
- это способ минимизировать и/или оптимизировать ваш запрос. То есть, без ключевого слова let
вам пришлось бы написать:
from x in list
where
SomeComplexExpressionDependingOnx > 10 &&
SomeComplexExpressionDependingOnx+5 < 50 &&
SomFunc(SomeComplexExpressionDependingOnx) == "str"
select x
В результате возможна тройная оценка одного и того же выражения.
Во-первых, что такого страшного в "блочных выражениях"? Это просто сокращение для произвольного делегата. То есть, следующее выражение:
Func<string,int> f =
s =>
{
var ln = s.Length;
return ln/2;
}
эквивалентно следующему:
int CompilerGeneratedMethodIdentifier0( string s )
{
var ln = s.Length;
return ln/2;
}
...
Func<string, int> f = new Func<string, int>( CompilerGeneratedMethodIdentifier0 );
Во-вторых, что такого особенного в "блочных выражениях"? Знаете ли вы, что ммм... назовем их "неблочные" выражения также расширяются до того же самого кода? То есть простой код new Func
абсолютно эквивалентен:
int CompilerGeneratedMethodIdentifier0( string s )
{
return s.Length/2;
}
...
new Func<string, int>( CompilerGeneratedMethodIdentifier0 );
Третье, что же такого не-linqy в "блочных выражениях"? LINQ использует делегаты повсюду, и для LINQ не имеет значения, какое именно сокращение вы используете для представления этих делегатов.
В частности, ваше выражение from a in list where a.SomeProp > 10 select new { A = a, B = a.GetB() }
переводится так:
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 ) );
Четвертое, чтобы добиться такого понимания, можно просто поиграть с языком и подумать. Использовать свой мозг, то есть.
И в-пятых, если предыдущий совет по тем или иным причинам вам не подходит, у вас всегда есть ILSpy. Очень полезный инструмент, он должен быть у каждого.
Просто предположение, поскольку я редко использую синтаксис запроса:
list.Select(x => new { v = SomeComplexExpressionDependingOnx(x) });
let просто назначает новую переменную v, а select возвращает ее.
Это также может быть следующее, если вы не хотите анонимный объект с v в нем:
var v = list.Select(x => SomeComplexExpressionDependingOnx(x));
list.Select(x => SomeComplexExpressionDependingOnx );
В общем, let
в основном работает как readonly
переменная, хранящая диапазон.
Взгляните на LINQPad , вы можете написать запрос и нажать на символ ламбы, чтобы увидеть, как будет выглядеть результат. Например, я взял этот запрос:
var names = new[] { "Tom", "Dick", "Harry", "Mary", "Jay" }.AsQueryable();
var results =
from n in names
let n1 = String.IsNullOrEmpty(n)
select n1;
results.Dump();
И он вывел следующее:
System.String[]
.Select (
n =>
new
{
n = n,
n1 = String.IsNullOrEmpty (n)
}
)
.Select (temp0 => temp0.n1)
Так что действительно похоже, что let переводится во временное значение как анонимное, а затем используется во внешнем операторе select.
Мне нравится LINQPad за возможность писать запросы и смотреть, как они будут переведены.