2011-12-27 6 views
10

ReSharper 6.0 fornisce l'avviso "Accesso alla chiusura modificata" per l'identificatore dr nel primo frammento di codice."Accesso alla chiusura modificata" risolto dalla sintassi di comprensione?

private IEnumerable<string> GetTheDataTableStrings(DataTable dt) { 
    foreach (DataRow dr in dt.Rows) { 
     yield return GetStringFuncOutput(() => dr.ToString()); 
    } 
} 

penso di avere una conoscenza di base di ciò che questo avvertimento sta cercando di proteggere me dal: dr cambia più volte prima uscita di GetTheDataTableStrings viene interrogato, e così il chiamante potrebbe non ottenere l'output/comportamento mi aspetto.

Ma R # non mi dà alcun avviso per il secondo snippet di codice.

private IEnumerable<string> GetTheDataTableStrings(DataTable dt) { 
    return from DataRow dr in dt.Rows select GetStringFuncOutput(dr.ToString); 
} 

E 'sicuro per me di scartare questo avvertimento/preoccupazione quando si utilizza la sintassi di comprensione?

Altro codice:

string GetStringFuncOutput(Func<string> stringFunc) { 
    return stringFunc(); 
} 
+0

Ho dovuto strofinare/semplificare questo codice prima di presentarlo. Fammi sapere se qualcosa sul codice ti impedisce di discutere la domanda. – lance

risposta

21

Prima di tutto, lei ha ragione di essere preoccupato per la prima versione. Ogni delegato creato da quel lambda viene chiuso sulla variabile stessa e pertanto al variare della variabile, il significato della query cambia.

In secondo luogo, è molto probabile che lo risolviamo nella prossima versione di C#; questo è un punto dolente per gli sviluppatori.

Nella prossima versione, ogni volta che si esegue il ciclo "foreach", si genera una variabile di ciclo anziché una chiusura della stessa variabile ogni volta. Questo è un cambiamento "di rottura", ma nella stragrande maggioranza dei casi la "rottura" si risolverà invece di causare bug.

Il ciclo "for" non verrà modificato.

Vedere http://ericlippert.com/2009/11/12/closing-over-the-loop-variable-considered-harmful-part-one/ per dettagli.

In terzo luogo, non esiste alcun problema con la versione di comprensione delle query poiché non esiste alcuna variabile chiusa che viene modificata. Il modulo di richiesta di comprensione è lo stesso che se avessi detto:

return dt.Rows.Select(dr=>GetStringFuncOutput(dr.ToString)); 

Il lambda non è chiuso su qualsiasi variabile esterna, quindi non c'è alcuna variabile da modificare accidentalmente.

+0

Risposta interessante e buone notizie sulle correzioni in C#. Quale versione ti aspetti di ottenere in C# 5 o 6? –

+1

@the_joric: nessun prodotto C# 6 è stato annunciato. Ci aspettiamo di mettere questa correzione in C# 5. (Abbiamo dovuto riscrivere comunque il codice di riscrittura della chiusura per rendere il lavoro asincrono/atteso, quindi figurato potrebbe anche farlo correggere allo stesso tempo.) –

+0

Grazie per l'aggiornamento Eric :). Questa è sicuramente una buona notizia. –

4

Il problema relativo all'avviso di Resharper è stato risolto sia in C# 5.0 che in VB.Net 11.0. I seguenti sono estratti dalle specifiche della lingua. Si noti che è possibile trovare le specifiche nei seguenti percorsi per impostazione predefinita su una macchina con Visual Studio 2012 installato.

  • C: \ Program Files (x86) \ Microsoft Visual Studio 11.0 \ VB \ Specifiche \ 1033 \ linguaggio Visual Basic Specification.docx
  • C: \ Program Files (x86) \ Microsoft Visual Studio 11.0 \ VC# \ Specifiche \ 1033 \ CSharp Lingua Specification.docx

del linguaggio C# Specification Version 5,0

8,8.4 Il foreach dichiarazione

Il posizionamento del v all'interno del ciclo, mentre è importante per il modo in cui viene catturata da qualsiasi funzione anonima che si verificano in-dichiarazione incorporata.

Ad esempio:

int[] values = { 7, 9, 13 }; 
Action f = null; 
foreach (var value in values) 
{ 
    if (f == null) f =() => Console.WriteLine("First value: " + value); 
} 
f(); 

Se v è stato dichiarato fuori del ciclo while, sarebbe essere condiviso tra tutte le iterazioni, e il suo valore dopo il ciclo for sarebbe il valore finale, 13 , che è ciò che l'invocazione di f stamperebbe. Invece, poiché ogni iterazione ha una propria variabile v, quella catturata da f nella prima iterazione continuerà a contenere il valore 7, che è ciò che verrà stampato. (Nota: le versioni precedenti di C# dichiarati v al di fuori del ciclo while.)

Il Microsoft Visual Basic Language Specification Version 11,0

10.9.3 For Each ... Dichiarazioni Avanti (Annotazione)

C'è un leggero cambiamento nel comportamento tra la versione 10.0 e 11.0 della lingua. Prima di 11.0, non è stata creata una nuova variabile di iterazione per ogni iterazione del ciclo. Questa differenza è osservabile solo se la variabile di iterazione viene catturata da un'espressione lambda o LINQ che viene quindi richiamata dopo il ciclo.

Dim lambdas As New List(Of Action) 
For Each x In {1,2,3} 
    lambdas.Add(Sub() Console.WriteLine(x) 
Next 
lambdas(0).Invoke() 
lambdas(1).Invoke() 
lambdas(2).Invoke() 

Fino a Visual Basic 10.0, questo ha prodotto un avvertimento al momento della compilazione e stampa "3" per tre volte. Questo perché c'era solo una variabile "x" condivisa da tutte le iterazioni del ciclo, e tutti e tre i lambda catturavano la stessa "x", e nel momento in cui venivano eseguiti i lambda, allora il numero 3. Come di Visual Basic 11.0, stampa "1, 2, 3". Questo perché ogni lambda cattura una variabile diversa "x".

Problemi correlati