Правильный Linq, где пункты

Я пишу огромное количество linq в моей повседневной жизни, но в основном простые заявления. Я заметил, что при использовании предложений where есть много способов написать их, и каждый из них имеет те же самые результаты, насколько я могу судить. Например:

from x in Collection
  where x.Age == 10
  where x.Name == "Fido"
  where x.Fat == true
  select x;

Кажется, это эквивалентно этому, по крайней мере, в отношении результатов:

from x in Collection
  where x.Age == 10 &&
        x.Name == "Fido" &&
        x.Fat == true
  select x;

Так есть ли разница, отличная от синтаксиса? Если да, то какой предпочтительный стиль и почему?

Ответ 1

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

Также цепочка (первый метод) будет работать только в том случае, если вы используете ANDing ваши предикаты. Что-то вроде этого x.Age == 10 || x.Fat == true не будет работать с вашим первым методом.

Ответ 2

EDIT: LINQ to Objects не ведет себя так, как я ожидал. Вы можете быть заинтересованы в сообщении в блоге Я только что написал об этом...


Они отличаются друг от друга тем, что будет называться - первое равнозначно:

Collection.Where(x => x.Age == 10)
          .Where(x => x.Name == "Fido")
          .Where(x => x.Fat == true)

поскольку последнее эквивалентно:

Collection.Where(x => x.Age == 10 && 
                      x.Name == "Fido" &&
                      x.Fat == true)

Теперь какая разница, которая действительно делает, зависит от реализации вызываемого Where. Если это поставщик на основе SQL, я бы ожидал, что двое в конечном итоге создадут тот же SQL. Если в LINQ to Objects, у второго будет меньше уровней косвенности (вместо четырех будут задействованы только два итератора). Независимо от того, являются ли эти уровни косвенности значимыми с точки зрения скорости, это другое дело.

Как правило, я бы использовал несколько предложений Where, если они чувствуют, что они представляют существенно разные условия (например, один из них связан с одной частью объекта, а другой - полностью отдельный) и один пункт Where, когда различные условия тесно связаны (например, конкретное значение больше минимального и меньше максимального). В принципе, стоит учитывать читаемость перед небольшими различиями в производительности.

Ответ 3

Первый будет реализован:

Collection.Where(x => x.Age == 10)
          .Where(x => x.Name == "Fido") // applied to the result of the previous
          .Where(x => x.Fat == true)    // applied to the result of the previous

В отличие от гораздо более простого (и гораздо быстрее предположительно быстрее):

// all in one fell swoop
Collection.Where(x => x.Age == 10 && x.Name == "Fido" && x.Fat == true)

Ответ 4

когда я запустил

from c in Customers
where c.CustomerID == 1
where c.CustomerID == 2
where c.CustomerID == 3
select c

и

from c in Customers
where c.CustomerID == 1 &&
c.CustomerID == 2 &&
c.CustomerID == 3
select c customer table in linqpad

в моей таблице Customer он выводит тот же запрос sql

-- Region Parameters
DECLARE @p0 Int = 1
DECLARE @p1 Int = 2
DECLARE @p2 Int = 3
-- EndRegion
SELECT [t0].[CustomerID], [t0].[CustomerName]
FROM [Customers] AS [t0]
WHERE ([t0].[CustomerID] = @p0) AND ([t0].[CustomerID] = @p1) AND ([t0].[CustomerID] = @p2)

поэтому в переводе на sql нет никакой разницы, и вы уже видели в других ответах, как они будут преобразованы в лямбда-выражения

Ответ 5

Подглядывая под капот, два оператора будут преобразованы в разные представления запросов. В зависимости от QueryProvider от Collection это может быть оптимизировано или нет.

Когда это вызов linq-to-object, несколько предложений where приведут к цепочке IEnumerables, которые читаются друг от друга. Использование формы с одним предложением поможет вам здесь.

Когда базовый поставщик переводит его в SQL-запрос, есть хорошие шансы, что оба варианта создадут один и тот же оператор.