Linq apropriada cláusulas where

133

Escrevo uma quantidade razoável de linq no meu dia-a-dia, mas principalmente declarações simples. Percebi que, ao usar as cláusulas where, há muitas maneiras de escrevê-las e cada uma tem os mesmos resultados, tanto quanto eu sei. Por exemplo;

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

Parece ser equivalente a isso, pelo menos no que diz respeito aos resultados:

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

Então, existe realmente outra diferença além da sintaxe? Se sim, qual é o estilo preferido e por quê?

AR
fonte
203
Você tem uma Fatpropriedade booleana ? Isso é maldade.
Bala R
104
@ Bala R: Ei, se seu cachorro é gordo, ele é gordo.
AR

Respostas:

76

O segundo seria mais eficiente, pois apenas possui um predicado para avaliar em relação a cada item da coleção. Como no primeiro, ele aplica o primeiro predicado a todos os itens primeiro e o resultado (que é reduzido nesse momento) é usado para o segundo predicado e assim por diante. Os resultados são reduzidos a cada passo, mas ainda envolvem vários passes.

Além disso, o encadeamento (primeiro método) funcionará apenas se você estiver ANDing com seus predicados. Algo assim x.Age == 10 || x.Fat == truenão funcionará com o seu primeiro método.

Bala R
fonte
1
Condições cadeia ORING é algo possível utilizar esta extensão: albahari.com/nutshell/predicatebuilder.aspx
jahu
142

EDIT: LINQ to Objects não se comporta como eu esperava. Você pode estar interessado no post que acabei de escrever sobre isso ...


Eles são diferentes em termos do que será chamado - o primeiro é equivalente a:

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

sendo que o último é equivalente a:

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

Agora, a diferença que realmente faz depende da implementação de Whereser chamado. Se for um provedor baseado em SQL, espero que os dois acabem criando o mesmo SQL. Se estiver no LINQ to Objects, o segundo terá menos níveis de indireção (haverá apenas dois iteradores envolvidos em vez de quatro). Se esses níveis de indireção são significativos em termos de velocidade é uma questão diferente.

Normalmente, eu usaria várias wherecláusulas se elas sentissem que estão representando condições significativamente diferentes (por exemplo, uma diz respeito a uma parte de um objeto e outra é completamente separada) e uma wherecláusula quando várias condições estão intimamente relacionadas (por exemplo, um valor específico é maior que um mínimo e menor que um máximo). Basicamente, vale a pena considerar a legibilidade antes de qualquer pequena diferença de desempenho.

Jon Skeet
fonte
1
@ JonSkeet Talvez eu esteja errado, mas após uma rápida revisão do Linq Where Implementation, não tenho certeza disso. Aninhado Onde são combinados pelo método estático 'CombinePredicates'. A coleção é iterada apenas uma vez por um único iterador com o predicado combinado. Obviamente, há um impacto no desempenho da combinação de funções, mas é muito limitado. Você está bem ?
precisa saber é o seguinte
@ Cybermaxs: Não tenho certeza do que , exatamente? Eu nunca sugeri que a coleção fosse iterada mais de uma vez.
perfil completo de Jon Skeet
@ JonSkeet sim, é claro, mas no final todos os predicados são combinados e apenas um iterador é invocado. OlheEnumerable.WhereSelectEnumerableIterator.
precisa saber é o seguinte
A página à qual você vinculou está desativada agora. Você poderia atualizar o link se o artigo ainda estiver em outro lugar? Obrigado.
Asad Saeeduddin
2
@Asad: Atualizado. (Meu blog foi movido.)
Jon Skeet
13

O primeiro será implementado:

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

Ao contrário do muito mais simples (e muito mais rápido, presumivelmente mais rápido):

// all in one fell swoop
Collection.Where(x => x.Age == 10 && x.Name == "Fido" && x.Fat == true)
user7116
fonte
6
"Muito mais rápido"? Ainda nem sabemos qual implementação do LINQ está envolvida, por isso é difícil anexar qualquer implicação de desempenho a ela.
Jon Skeet
No caso geral, o último requer apenas 1 loop. Um provedor pode optar por achatar o primeiro exemplo, mas não é necessário.
user7116
2
Na verdade ... mas você está afirmando que o último é muito mais rápido. Não está nada claro que será significativamente mais rápido - afinal, o significado da diferença de desempenho dependerá de como isso está sendo usado.
Jon Skeet
1
@ Jon: sem desacordo. Conforme você observa a realidade, o provedor LINQ segue e realiza transformações de otimização úteis na expressão. Mas, como o segundo exige apenas um loop e se beneficia do curto-circuito booleano, é difícil entender por que não deve ser rotulado como "muito mais rápido" em termos gerais. Se o OP tiver apenas 5 elementos, meu ponto é discutível.
user7116
11

quando eu corro

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

e

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

na minha tabela Customer, ele produz a mesma consulta 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)

portanto, na tradução para o sql não há diferença e você já viu em outras respostas como elas serão convertidas em expressões lambda

Muhammad Adeel Zahid
fonte
ok, então você quer dizer que não terá nenhum efeito no desempenho se eu usar algum desses?
Bimal Das
Cláusulas WHERE são encadeadas de fato. Então, não importa como você escreve. Não há diferença de desempenho.
hastrb
3

Olhando sob o capô, as duas instruções serão transformadas em diferentes representações de consulta. Dependendo QueryProviderdo Collection, isso pode ser otimizado ou não.

Quando se trata de uma chamada linq para objeto, várias cláusulas where levarão a uma cadeia de IEnumerables que são lidas uma da outra. Usar o formulário de cláusula única ajudará o desempenho aqui.

Quando o provedor subjacente o converte em uma instrução SQL, são grandes as chances de as duas variantes criarem a mesma instrução.

David Schmitt
fonte