Почему этот запрос LINQ компилируется? - PullRequest
5 голосов
/ 01 октября 2010

Прочитав " Нечетные выражения запроса " Джона Скита, я попробовал приведенный ниже код. Я ожидал, что запрос LINQ в конце переведет в int query = proxy.Where(x => x).Select(x => x);, который не компилируется, потому что Where возвращает int. Код скомпилирован и выводит «Where (x => x)» на экран, а запрос устанавливается на 2. Выбор никогда не вызывается, но он должен быть там, чтобы код компилировался. Что происходит?

using System;
using System.Linq.Expressions;

public class LinqProxy
{
    public Func<Expression<Func<string,string>>,int> Select { get; set; }
    public Func<Expression<Func<string,string>>,int> Where { get; set; }
}

class Test
{
    static void Main()
    {
        LinqProxy proxy = new LinqProxy();

        proxy.Select = exp => 
        { 
            Console.WriteLine("Select({0})", exp);
            return 1;
        };
        proxy.Where = exp => 
        { 
            Console.WriteLine("Where({0})", exp);
            return 2;
        };

        int query = from x in proxy
                    where x
                    select x;
    }
}

Ответы [ 2 ]

12 голосов
/ 01 октября 2010

Это потому, что ваш "select x" фактически не используется - компилятор даже не ставит вызов Select(x => x) в конце.Однако будет , если вы удалите предложение where.Ваш текущий запрос известен как вырожденное выражение запроса .См. Раздел 7.16.2.3 спецификации C # 4 для более подробной информации.В частности:

Вырожденное выражение запроса - это то, которое тривиально выбирает элементы источника.Более поздняя фаза перевода удаляет вырожденные запросы, введенные другими шагами перевода, заменяя их источником.Однако важно убедиться, что результатом выражения запроса никогда не будет сам исходный объект, так как это выявит тип и идентичность источника для клиента запроса.Поэтому этот шаг защищает вырожденные запросы, написанные непосредственно в исходном коде, явно вызывая Select для источника.Затем разработчики Select и других операторов запросов должны убедиться, что эти методы никогда не вернут сам исходный объект.

Итак, три перевода (независимо от источника данных)

// Query                          // Translation
from x in proxy                   proxy.Where(x => x)
where x
select x


from x in proxy                   proxy.Select(x => x)
select x               


from x in proxy                   proxy.Where(x => x)
where x                                .Select(x => x * 2)
select x * 2
7 голосов
/ 01 октября 2010

Компилируется, потому что синтаксис запроса LINQ представляет собой лексическую замену .Компилятор превращает

int query = from x in proxy
            where x
            select x;

в

int query = proxy.Where(x => x);     // note it optimises the select away

и только , затем проверяет, действительно ли существуют методы Where и Select для типаproxy.Соответственно, в приведенном вами конкретном примере Select фактически не требуется для компиляции.

Если бы у вас было что-то вроде этого:

    int query = from x in proxy
                select x.ToString();

, тогда это изменилось быв:

int query = proxy.Select(x => x.ToString());

и будет вызван метод Select.

...