2011-12-27 15 views
10

ReSharper 6.0 me da la advertencia "Acceso al cierre modificado" para el identificador dr en el primer fragmento de código.¿Se resuelve el "Acceso al cierre modificado" por sintaxis de comprensión?

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

creo que tengo una comprensión básica de lo que esta advertencia está tratando de protegerme de: dr cambia varias veces antes de que se interroga de salida de GetTheDataTableStrings, por lo que la persona que llama no puede obtener la salida/comportamiento que espero.

Pero R # no me da ninguna advertencia para el segundo fragmento de código.

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

¿Es seguro para mí descartar esta advertencia/preocupación al usar la sintaxis de comprensión?

Otro código:

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

Tuve que eliminar/simplificar este código antes de presentarlo. Avíseme si algo sobre el código le impide hablar sobre la pregunta. – lance

Respuesta

21

En primer lugar, tienes razón para estar preocupado por la primera versión. Cada delegado creado por esa lambda se cierra sobre la misma variable y, por lo tanto, a medida que esa variable cambia, cambia el significado de la consulta.

En segundo lugar, para su información, es muy probable que solucionemos esto en la próxima versión de C#; este es un gran punto de dolor para los desarrolladores.

En la siguiente versión, cada vez que se ejecuta el ciclo "foreach" generaremos una nueva variable de bucle en lugar de cerrar la misma variable cada vez. Este es un cambio de "ruptura", pero en la gran mayoría de los casos el "rompimiento" será la solución en lugar de causar errores.

El bucle "for" no cambiará.

Ver http://ericlippert.com/2009/11/12/closing-over-the-loop-variable-considered-harmful-part-one/ para más detalles.

En tercer lugar, no hay ningún problema con la versión de comprensión de consultas porque no hay una variable cerrada que se esté modificando. La forma de comprensión consulta es el mismo que si hubieras dicho:

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

la lambda no se cierra sobre cualquier variable externa, por lo que no hay una variable que ser modificado accidentalmente.

+0

Respuesta genial y buenas noticias sobre las correcciones en C#. ¿Qué versión esperas obtener - C# 5 o 6? –

+1

@the_joric: No se ha anunciado ningún producto C# 6. Esperamos poner esta corrección en C# 5. (Tuvimos que volver a configurar el código de reescritura de todos modos para hacer el trabajo asincrónico/aguardar, por lo que pensamos que podría arreglar esto al mismo tiempo). –

+0

Gracias por la actualización de Eric :). Esa es definitivamente una buena noticia. –

4

El problema que Resharper está advirtiendo se ha resuelto tanto en C# 5.0 como en VB.Net 11.0. Los siguientes son extractos de las especificaciones del idioma. Tenga en cuenta que las especificaciones se pueden encontrar en las siguientes rutas de manera predeterminada en una máquina con Visual Studio 2012 instalado.

  • C: \ Archivos de programa (x86) \ Microsoft Visual Studio 11.0 \ VB \ Especificaciones \ 1033 \ lenguaje Visual Basic Specification.docx
  • C: \ Archivos de programa (x86) \ Microsoft Visual Studio 11.0 \ VC# \ Especificaciones \ 1033 \ CSharp idioma Specification.docx

C# Language Specification Version 5,0

8,8.4 La declaración

La colocación de v dentro del bucle mientras foreach es importante para la forma en que es capturado por cualquier función anónima que ocurre en la instrucción incrustada.

Por ejemplo:

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

Si v se declara fuera del bucle while, que sería compartido entre todas las iteraciones, y su valor después de que el bucle for sería el valor final, 13 , que es lo que imprimiría la invocación de f. En cambio, dado que cada iteración tiene su propia variable v, la capturada por f en la primera iteración continuará manteniendo el valor 7, que es lo que se imprimirá. (Nota: Las versiones anteriores de C# v declarados fuera del bucle while.)

El Microsoft Visual Basic Language Specification Version 11,0

10.9.3 For Each ... Next declaraciones (anotación)

Hay un ligero cambio en el comportamiento entre la versión 10.0 y la 11.0 del idioma. Antes de 11.0, no se creó una nueva variable de iteración para cada iteración del ciclo. Esta diferencia es observable solo si la variable de iteración es capturada por una lambda o una expresión LINQ que luego se invoca después del bucle.

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() 

Hasta Visual Basic 10.0, esto produjo una advertencia en tiempo de compilación e impresos "3" tres veces. Eso fue porque solo había una sola variable "x" compartida por todas las iteraciones del ciclo, y las tres lambdas capturaron la misma "x", y para el momento en que se ejecutaron las lambdas, entonces contenía el número 3. A partir de Visual Basic 11.0, imprime "1, 2, 3". Eso es porque cada lambda captura una variable diferente "x".

Cuestiones relacionadas