2010-05-13 18 views
5

Quando eseguo questo frammento di codice:Perché i miei parametri associati sono tutti identici (usando Linq)?

string[] words = new string[] { "foo", "bar" }; 
var results = from row in Assets select row; 
foreach (string word in words) 
{ 
    results = results.Where(row => row.Name.Contains(word)); 
} 

ottengo questo SQL:

-- Region Parameters 
DECLARE @p0 VarChar(5) = '%bar%' 
DECLARE @p1 VarChar(5) = '%bar%' 
-- EndRegion 
SELECT ... FROM [Assets] AS [t0] 
WHERE ([t0].[Name] LIKE @p0) AND ([t0].[Name] LIKE @p1) 

noti che @p0 e @p1 sono entrambi bar, quando ho voluto che fossero foo e bar.

Immagino che Linq legga in qualche modo un riferimento alla variabile word piuttosto che un riferimento alla stringa attualmente indicata da word? Qual è il modo migliore per evitare questo problema?

(Inoltre, se avete qualche suggerimento per un titolo migliore per questa domanda, si prega di inserire nei commenti.)

Nota che ho provato questo con regolare Linq anche, con gli stessi risultati (è possibile incollare questo diritto in LINQPad):

string[] words = new string[] { "f", "a" }; 
string[] dictionary = new string[] { "foo", "bar", "jack", "splat" }; 
var results = from row in dictionary select row; 
foreach (string word in words) 
{ 
    results = results.Where(row => row.Contains(word)); 
} 
results.Dump(); 

Discariche:

bar 
jack 
splat 
+1

Per ulteriori commenti e analisi di questo problema vedere http://blogs.msdn.com/ericlippert/archive/2009/11/12/closing-over-the-loop-variable-considered- nocivo.aspx e http://blogs.msdn.com/ericlippert/archive/2009/11/16/closing-over-the-loop-variable-part-two.aspx –

risposta

8

si sta utilizzando quello che viene chiamato un "chiusura", il che significa che si sta definendo una funzione anonima (il vostro lambda) che utilizza una variabile locale nel suo corpo. Nello specifico, stai "chiudendo" la variabile del ciclo word. Il problema che si verifica con i risultati delle chiusure da in esecuzione ritardata è, il che significa che il corpo del tuo lambda non viene eseguito quando lo definisci, ma quando viene invocato.

Per questo motivo, quasi mai si desidera chiudere la variabile del ciclo. A causa dell'esecuzione ritardata, il valore della variabile all'interno del lambda sarà qualunque sia quando viene eseguita la lambda. Per lambdas dichiarato all'interno di un ciclo e invocato al di fuori di esso, ciò significa che avrà sempre l'ultimo valore dal ciclo.

Per contrastare ciò, utilizzare una variabile locale dichiarata all'interno del ciclo. Questo farà sì che acquisisca il valore in quel momento e passi una nuova variabile ad ogni lambda che viene creato. In questo modo:

string[] words = new string[] { "foo", "bar" }; 
var results = from row in Assets select row; 
foreach (string word in words) 
{ 
    string tempWord = word; 

    results = results.Where(row => row.Name.Contains(tempWord)); 
} 
+0

Grazie, descrizione eccellente e funziona anche! :) –

Problemi correlati