2013-02-27 11 views
35

prima dichiarazione:Perché queste uscite linq sono diverse?

IEnumerable<char> query = "Not what you might expect"; 

query = query.Where (c => c != 'a'); 
query = query.Where (c => c != 'e'); 
query = query.Where (c => c != 'i'); 
query = query.Where (c => c != 'o'); 
query = query.Where (c => c != 'u'); 

uscita di String.Join("", query): "Nt wht y mght xpct"

seconda affermazione:

query = "Not what you might expect"; 

foreach (char vowel in "aeiou") 
    query = query.Where (c => c != vowel); 

uscita di String.Join("", query): "Not what yo might expect"

Le uscite di queste affermazioni sono diverse. Qualcuno può spiegare perché?

+4

qual è l'output che ottieni? – Default

+1

I risultati dipenderanno da quale versione di .NET si è scelto come target, a quale versione è contro? – goric

+1

È davvero così strutturato il tuo codice? Il valore 'vowel' deve essere rimosso nel secondo esempio, altrimenti verrà eseguito come'! = 'U'' 5 volte. –

risposta

57

Se stai usando un C# versione inferiore a 5.0 (in cui questo è stato fissato), questo è il motivo:

Il lambda nella query cattura l'indice del ciclo vowel.
Poiché a Linq piace eseguire l'esecuzione posticipata, il valore di questo riferimento non viene letto fino a quando la query non viene eseguita (eseguendo un'iterazione su di esso), ovvero dopo che il ciclo foreach è terminato. A quel punto, il valore più recente di vowel è u, motivo per cui si ottiene l'output imprevisto.

È possibile aggirare questo copiando il valore su un'altra variabile temporanea (o eseguendo l'aggiornamento a C# 5.0).

Prova questo:

query = "Probably what you might expect"; 

foreach (char vowel in "aeiou") { 
    char currentVowel = vowel; 
    query = query.Where (c => c != currentVowel); 
} 
+0

Solo curioso, quando C# ha risolto questo problema? Dove posso leggere questa correzione? Grazie. –

+3

Oh, [vedo] (http://blogs.msdn.com/b/ericlippert/archive/2009/11/12/closeoverover-the-loop-variable-considered-harmful.aspx), in C# 5 –

+2

Chiedersi perché questo è stato "corretto" in .net 4.5. Poiché LINQ è posticipato, per definizione, non vedo perché modificare il vecchio comportamento, che in realtà è più corretto. – Teejay

11

Leggi su chiusura. Se usi .NET 4.0 e sotto otterrai risultati diversi. In .NET 4.5 questo comportamento è stato modificato (risolto). Vedi anche come espandere il compilatore foreach.

+0

Come per questo articolo http://msmvps.com/blogs/kathleen/archive/2012/07/03/lifting-foreach-breaking-change-in-visual-studio-2012.aspx sembra essere un cambiamento in VS Compilatore 2012, piuttosto che in C# 5. Infatti, ho appena provato con VB sul framework 3.5 e fornisce il nuovo risultato * previsto *. Si prega di modificare la risposta per non confondere i futuri lettori. – Teejay

+1

@Teejay Questo comportamento è descritto nella specifica C# 5.0. Anche i compilatori fanno parte di .NET Framework. –

+1

Probabilmente. Ma hai scritto * "Se usi .NET 4.0 e sotto otterrai risultati diversi." *. Non è vero. Appena provato anche su 3.5 e si comporta come il nuovo 4.5. – Teejay

13

È perché si crea una chiusura sulla variabile vowel, che cambia nel tempo. Memorizza il suo valore in una variabile separata e funzionerà:

query = "Not what you might expect"; 

foreach (char vowel in "aeiou") 
{ 
    var current = vowel; 
    query = query.Where (c => c != current); 
} 
Problemi correlati