Linq corretto dove clausole


133

Scrivo una buona quantità di linq nella mia vita quotidiana, ma soprattutto dichiarazioni semplici. Ho notato che quando si usano clausole where, ci sono molti modi per scriverle e ognuna ha gli stessi risultati per quanto ne so. Per esempio;

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

Sembra essere equivalente a questo almeno per quanto riguarda i risultati:

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

Quindi c'è davvero una differenza diversa dalla sintassi? In tal caso, qual è lo stile preferito e perché?


203
Hai una Fatproprietà booleana ? Questo è semplicemente cattivo.
Bala R,

104
@Bala R: Ehi, se il tuo cane è grasso, il tuo cane è grasso.
AR

Risposte:


76

Il secondo sarebbe più efficiente in quanto ha un solo predicato da valutare rispetto a ciascun elemento della raccolta in cui, come nel primo, applica prima il predicato a tutti gli elementi e il risultato (che è ristretto a questo punto) è usato per il secondo predicato e così via. I risultati vengono ridotti ad ogni passaggio, ma comporta comunque più passaggi.

Anche il concatenamento (primo metodo) funzionerà solo se stai ANDando i tuoi predicati. Qualcosa del genere x.Age == 10 || x.Fat == truenon funzionerà con il tuo primo metodo.


1
Le condizioni di ORing della catena sono in qualche modo possibili usando questa estensione: albahari.com/nutshell/predicatebuilder.aspx
jahu

142

EDIT: LINQ to Objects non si comporta come mi aspettavo. Potresti essere interessato al post sul blog che ho appena scritto su questo ...


Sono diversi in termini di ciò che verrà chiamato - il primo equivale a:

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

dove quest'ultimo equivale a:

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

Ora quale differenza fa effettivamente dipende dall'implementazione di Whereessere chiamato. Se è un provider basato su SQL, mi aspetto che i due finiscano per creare lo stesso SQL. Se è in LINQ to Objects, il secondo avrà meno livelli di indiretta (ci saranno solo due iteratori invece di quattro). Se questi livelli di riferimento indiretto siano significativi in termini di velocità è una questione diversa.

In genere utilizzerei diverse whereclausole se si sentono come se rappresentassero condizioni significativamente diverse (ad esempio, uno ha a che fare con una parte di un oggetto e uno è completamente separato) e una whereclausola quando varie condizioni sono strettamente correlate (ad esempio un valore particolare è maggiore di un minimo e inferiore a un massimo). Fondamentalmente vale la pena considerare la leggibilità prima di ogni leggera differenza di prestazioni.


1
@JonSkeet Forse mi sbaglio, ma dopo una breve revisione di Linq Where implementazione, non ne sono sicuro. Nidificato Dove sono combinati con un metodo statico "CombinePredicates". La raccolta viene ripetuta una sola volta da un singolo iteratore con predicato combinato. Naturalmente, la combinazione di funzioni ha un impatto sulle prestazioni, ma è molto limitata. Stai bene ?
Cybermaxs

@Cybermaxs: Non sei sicuro di cosa , precisamente? Non ho mai suggerito che la collezione sarebbe stata ripetuta più di una volta.
Jon Skeet,

@JonSkeet sì, ma alla fine tutti i predicati vengono combinati e viene coinvolto un solo iteratore. Guarda Enumerable.WhereSelectEnumerableIterator.
Cybermaxs

La pagina a cui ti sei collegato ora è inattiva. Potresti aggiornare il link se l'articolo è ancora in qualche altro posto? Grazie.
Asad Saeeduddin,

2
@Asad: aggiornato. (Il mio blog si è spostato.)
Jon Skeet,

13

Il primo sarà implementato:

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

Al contrario del molto più semplice (e molto più veloce presumibilmente più veloce):

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

6
"Molto più veloce"? Non sappiamo nemmeno quale implementazione LINQ sia ancora coinvolta, quindi è difficile associare ad essa implicazioni in termini di prestazioni.
Jon Skeet,

Nel caso generale quest'ultimo richiede solo 1 loop. Un provider potrebbe scegliere di appiattire il primo esempio, ma non è necessario.
user7116,

2
Anzi ... ma stai affermando che quest'ultimo è molto più veloce. Non è affatto chiaro che sarà significativamente più veloce - dopotutto, il significato della differenza di prestazioni dipenderà da come viene utilizzato.
Jon Skeet

1
@Jon: nessun disaccordo. Come notate, la realtà potrebbe essere il provider LINQ che fa utili trasformazioni di ottimizzazione per l'espressione. Ma dato che il secondo richiede solo un loop e beneficia del corto circuito booleano, è difficile capire perché non dovrebbe essere etichettato come "molto più veloce" in termini generali. Se l'OP ha solo 5 elementi, il mio punto è controverso.
user7116,

11

quando 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

contro la mia tabella clienti ha generato la stessa query 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)

quindi nella traduzione in sql non c'è alcuna differenza e hai già visto in altre risposte come verranno convertiti in espressioni lambda


ok, allora vuoi dire che non avrà alcun effetto sulle prestazioni se uso una di queste?
Bimal Das,

Dunque le clausole sono concatenate. Quindi, non importa come lo scrivi. Non c'è differenza di prestazioni.
hastrb,

3

Guardando sotto il cofano, le due affermazioni verranno trasformate in diverse rappresentazioni di query. Dipende daQueryProvider diCollection , questo potrebbe essere ottimizzato o meno.

Quando si tratta di una chiamata linq-to-object, più clausole where porteranno a una catena di IEnumerables che leggono l'una dall'altra. L'uso del modulo a clausola singola aiuterà le prestazioni qui.

Quando il provider sottostante lo traduce in un'istruzione SQL, è probabile che entrambe le varianti creino la stessa istruzione.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.