resumen conflicto campo c# class lambda field increment

c# - conflicto del campo 2008 resumen



ParĂ¡metro Lambda en conflicto con el campo de clase al acceder al campo en el alcance posterior (3)

Tengo una imaginación débil cuando se trata de nombres, por lo que a menudo me encuentro reutilizando identificadores en mi código. Esto hizo que me encontré con este problema específico.

Aquí hay un código de ejemplo:

public delegate void TestDelegate(int test); public class Test { private int test; private void method(int aaa) { TestDelegate del = test => aaa++; test++; } public static void Main() { } }

Aquí están los errores de compilación (salida por ideone ):

prog.cs(11,3): error CS0135: `test'' conflicts with a declaration in a child block prog.cs(9,22): (Location of the symbol related to previous error) Compilation failed: 1 error(s), 0 warnings

La línea 11 contiene test++ , la línea 9 contiene la lambda.

Por cierto, Visual Studio 2013 da un error diferente:

''test'' conflicts with the declaration ''Namespace.Test.test''

El error se produce en el incremento en la línea 11 solamente.

El código se compila correctamente si comento la línea 9 (la lambda) o la línea 11 (el incremento).

Este problema es una sorpresa para mí: estaba seguro de que los nombres de los parámetros de lambda solo pueden entrar en conflicto con los nombres de las variables de los métodos locales (lo cual es confirmado por el código que compila cuando comento el incremento). Además, ¿cómo puede el parámetro lambda posiblemente afectar el incremento, que está justo fuera del alcance de la lambda?

No puedo entender esto ... ¿Qué fue exactamente lo que hice mal? ¿Y qué significan los mensajes de error crípticos en este caso?

EDITAR después de todas las grandes respuestas:

Así que creo que finalmente entendí la regla que rompí. No está bien redactado en la especificación de C # (7.6.2.1, vea la respuesta de Jon Skeet para la cita). Lo que se suponía que significaba es algo como:

No puede usar el mismo identificador para referirse a diferentes cosas (entidades) en el mismo "espacio de declaración de variable local" si uno de los usos infractores está (directamente) ubicado en un ámbito que puede ser "visto" desde el ámbito donde el otro Se encuentra (directamente) .

No es el fraseo estándar, pero espero que haya entendido lo que quiero decir. Se suponía que esta regla permitía esto:

{ int a; } { int a; }

porque ninguno de los ámbitos de las dos variables a puede ser "visto" desde el alcance de la otra;

y no permitir esto:

{ int a; } int a;

porque la segunda declaración de variable se "ve" desde el alcance de la primera variable

y no permitir esto:

class Test { int test; void method() { { int test; } test++; } }

porque el incremento del campo se puede "ver" desde el alcance del bloque (no es una declaración no importa).

Parece que C # 6 cambió esta regla, específicamente haciendo que el último ejemplo (y mi código original) sea legítimo, aunque realmente no entiendo cómo.

Por favor corríjame si cometí algunos errores en estos ejemplos.


La respuesta desafortunada de Sriram Sakthivel es correcta. C # tiene una regla, que he escrito varias veces, que requiere que cada uso del mismo nombre simple a lo largo de un bloque tenga el mismo significado.

Estoy de acuerdo en que el mensaje de error es extremadamente confuso. Hice un extenso trabajo en Roslyn para garantizar que este mensaje de error fuera menos confuso en Roslyn, trabajo que puede haber sido en vano.

Puede leer mis artículos sobre esta regla y el trabajo que hice para mejorar el mensaje de error, aquí:

http://ericlippert.com/tag/simple-names/

(Comience en la parte inferior; estos están en orden cronológico inverso).

Jon y otros señalan correctamente que en las versiones preliminares de C # 6, no aparece el error para su código. Creo que después de dejar el equipo, se hizo más trabajo en esta condición de error, y es probable que se haya relajado para ser más indulgente.

El principio de "un nombre debe significar solo una cosa" es bueno, pero es difícil de implementar, complicado de explicar y es la fuente de muchas preguntas en este sitio, por lo que probablemente el equipo de diseño decidió ir con un Regla más fácil de explicar y de implementar. Qué es exactamente esa regla, no lo sé; Todavía no he tenido tiempo de examinar el código fuente de Roslyn y ver cómo esa parte del código ha evolucionado con el tiempo.

ACTUALIZACIÓN: Mis espías en el equipo de Roslyn me informan que se cometió 23891e, lo que acortará la búsqueda considerablemente. :-)

ACTUALIZACIÓN: La regla se ha ido para siempre; ver la respuesta de Jon para más detalles.


Presenté el número 2110 de Roslyn para esto: la especificación C # 6 está cambiando para permitir esto. Mads Torgersen ha indicado que el cambio es por diseño:

La regla estaba bien intencionada, ya que se suponía que minimizaba el riesgo de "mover el código" de una manera que cambiaría su significado en silencio. Sin embargo, todos con quienes hablamos solo parecían saber acerca de la regla de cuando les estaba causando una pena innecesaria: nadie había sido salvado por sus prohibiciones.

Además, el seguimiento adicional necesario en el compilador para hacer cumplir la regla de manera interactiva estaba causando complicaciones significativas en el código, así como una sobrecarga de rendimiento no trivial. Ahora, si fuera una buena regla, la habríamos mantenido de todos modos, ¡pero no lo es! Así que se ha ido.

Es sencillo demostrar la inconsistencia entre las versiones del compilador sin ningún delegado involucrado:

class Test { static int test; static void Main() { { int test = 10; } test++; } }

La sección relacionada de la especificación C # 5 es 7.6.2.1, que da esta regla:

7.6.2.1 Significado invariante en bloques

Para cada aparición de un identificador dado como un nombre simple completo (sin una lista de argumentos de tipo) en una expresión o declarador, dentro del espacio de declaración de variable local (§3.3) que encierra esa ocurrencia, cada otra ocurrencia del mismo identificador como El nombre simple completo en una expresión o declarador debe referirse a la misma entidad. Esta regla garantiza que el significado de un nombre sea siempre el mismo dentro de un bloque dado, bloque de cambio, instrucción for, foreach o using, o función anónima.

Personalmente, creo que esto es un poco menos que ideal de especificaciones. No está claro si "dentro de un bloque dado" está destinado a incluir bloques anidados. Por ejemplo, esto está bien:

static void Main() { { int x = 10; } { int x = 20; } }

... a pesar del hecho de que x se usa para referirse a diferentes variables dentro del bloque de "nivel superior" del método Main , si incluye el anidamiento. Por lo tanto, si considera ese bloqueo, viola la afirmación de que "esta regla garantiza que el significado de un nombre sea siempre el mismo dentro de un bloque determinado". Sin embargo, creo que ese bloqueo no se verifica, porque no es el espacio de declaración variable "inmediatamente encerrado" de cualquier uso de x .

Entonces, en mi opinión, el error es consistente con la primera parte citada de la especificación, pero no es consistente con la oración final, que es una nota en la especificación ECMA.

Tomaré nota de la mala redacción de la especificación e intentaré corregirla para la próxima versión de la especificación ECMA.


Eric Lippert ha escrito en su blog sobre esto. Los nombres simples no son tan simples .

Los nombres simples (sin un nombre completo) siempre pueden significar una sola cosa en un bloque de código. Si lo viola, habrá un error del compilador CS0135 .

En su método de method , la test es un nombre simple, lo que significa dos cosas. No está permitido en c #.

Si realiza el campo de prueba, utiliza un nombre calificado en lugar de un nombre simple, el error del compilador desaparecerá.

private void method(int aaa) { TestDelegate del = test => aaa++; this.test++; }

O, si realiza el acceso al campo de prueba en un bloque diferente, el compilador estará encantado de compilar.

private void method(int aaa) { TestDelegate del = (int test) => aaa++; { test++; } }

Ahora no tiene dos significados diferentes para la misma test nombre simple. Porque la segunda prueba vive en un bloque diferente.

A partir de ahora (abril 2015) esta respuesta es válida. A partir de C # 6.0 las cosas han cambiado. Esta regla se ha ido. Consulte la respuesta de Jon para más detalles.