LINQ To Object выполняет какую-либо внутреннюю настройку? - PullRequest
3 голосов
/ 15 февраля 2011

Интересно, как LINQ to Objects определяет лучший способ запроса в коллекции?

Я имею в виду, есть ли разница (в том, как работает LINQ внутри) между следующими запросами LINQ:

var lst1 = ListOfComplexClass.Where(p => p.StrValue == "Whatever")
                             .Select(p => p.StrValue);

И

var lst2 = ListOfComplexClass.Select(p => p.StrValue)
                             .Where(p => p == "Whatever");

Например, будет ли первый фильтровать коллекцию ListOfComplexClass, а затем получит свойство StrValue? Или он выполнит некоторую настройку и сначала выполнит Select, а затем отфильтрует возвращенную коллекцию?

Ответы [ 5 ]

2 голосов
/ 15 февраля 2011

Например .. будет ли первый фильтровать коллекцию ListOfComplexClass, а затем получит свойство StrValue? Или, может быть, произойдет некоторая настройка и сначала выполнение Select, а затем фильтрация возвращенной коллекции?

(я предполагаю, что вы имели в виду p => p.StrValue в Select в первом.)

На самом деле, это более тонко, чем это.

Допустим, вы берете перечислитель на первом.

var lst1 = ListOfComplexClass.Where(p => p.StrValue == "Whatever")
                             .Select(p => p.StrValue);
var e = lst1.GetEnumerator();

Когда вы вызываете e.MoveNext(), происходит то, что Select будет вызывать MoveNext() на итераторе ListOfComplexClass.Where(p => p.StrValue == "Whatever"), который будет вызывать MoveNext() итератором ListOfComplexClass, пока не найдет элемент p с p.StrValue == "Whatever". Затем прогнозируемый результат этого p будет возвращен как e.Current.

Теперь давайте рассмотрим второй. Допустим, вы берете его перечислитель.

var lst2 = ListOfComplexClass.Select(p => p.StrValue)
                             .Where(p => p == "Whatever");
var e = lst2.GetEnumerator();

Когда вы вызываете e.MoveNext(), Where будет вызывать MoveNext() на итераторе ListOfComplexClass.Select(p => p.StrValue), пока не найдет элемент p с p == "Whatever". Конечно, вызов MoveNext() для итератора ListOfComplexClass.Select(p => p.StrValue) вызовет MoveNext() для итератора ListOfComplexClass и вернет проекцию Current для этого итератора.

У Джона Скита есть хорошая аналогия, которую я здесь украду. Представьте колоду карт в случайном порядке. Представьте себе запросы

var suits = deck.Where(c => c.Suit == Suit.Diamond || c.Suit == Suit.Heart)
                .Select(c => c.Suit)

и

var suits = deck.Select(c => c.Suit)
                .Where(c => c == Suit.Diamond || c == Suit.Heart);

Теперь представьте, что вы потребляете результат первого запроса. Это идет так.

Select просит Where карту.

Where просит deck для карты.

*Where проверяет масть карты.

Если масть карты - бриллиант или сердце, она возвращает карту Select и Select проецирует карту в свою масть и возвращает ее.

Если масть карты не алмаз или сердце, Where запрашивает другую карту из колоды и возвращается к *.

Второй запрос выглядит следующим образом.

Where просит Select костюм.

Select просит deck для карты.

deck возвращает карту для выбора.

Select проецирует карту на свой костюм.

*Where проверяет костюм от Select.

Если костюм бриллиант или сердце, Where возвращает костюм.

Если костюм не бриллиант или сердце, Where просит Select другой костюм и возвращается к *.

2 голосов
/ 15 февраля 2011

Интересно, что LINQ to Object ищет лучший способ сделать запрос в коллекции?

Нет, не смог.Здесь нет «двигателя».Звонки работают друг с другом на выходе в прямой цепочке.Если второе быстрее, чем это оптимизация, вам придется сделать.

Возможно, что Linq-to-SQL или Linq-to-Entities (IQueryable) могли бы что-то оптимизировать, но не Linq-to-Objects.

0 голосов
/ 15 февраля 2011

В случае, если вы представляете, нет никакой разницы.

Но если вы поместите ToList в ToArray где-то там, это может иметь значение.

LINQ to Objects, как и LINQ to SQL, по-прежнему использует ленивое выполнение, даже если оно просто работает в объектах памяти.

Это потому, что расширения LINQ в IEnumerable используют деревья выражений. Это означает, что ваши выражения LINQ выполняются только тогда, когда это необходимо.

обычно, если вы объединяете расширения IEnumerable, это не имеет значения. однако, если вам нужно изменить типы наполовину или вызвать что-то вроде ToList, это изменит порядок выполнения.

0 голосов
/ 15 февраля 2011

Linq для объектов не использует выражения типа linq to sql, поэтому для оптимизации подобного места не существует - каждая команда linq возвращает другое перечисляемое значение или значение. Может быть некоторая оптимизация при использовании синтаксиса from .. in .. where .. select .. компилятором, но я бы не ожидал ничего сложного. В вашем примере использование where сначала должно быть немного быстрее (по крайней мере в теории), поскольку переход между where и select уже будет отфильтрован, но я не думаю, что мы можем даже измерить такую ​​разницу, поскольку она действительно просто передает значения между вызывающим и вызываемым. Однако, если вы будете каким-то образом преобразовывать выбранное значение, разница будет заметна, поскольку вы будете создавать новый объект в уже отфильтрованной коллекции.

0 голосов
/ 15 февраля 2011

В данном конкретном случае это не имеет значения.Вы проецируете строку напрямую, поэтому новый объект не создается, и у вас остается только одно сравнение.С другой стороны, могут быть случаи (проецирование в другой тип), где это может иметь значение.Я не верю, что LINQ попытается оптимизировать прогнозы, поскольку они могут содержать побочные эффекты.

...