visual utiliza sharp que programacion para lenguaje informacion evolucion antecedentes c# language-lawyer member-access

c# - utiliza - lenguaje de programacion c



¿El compilador de C#obtiene la regla Color Color incorrecta con los miembros de tipo const? (3)

1) No funciona con un const porque intenta permitir ambas definiciones (tipo de enumeración y miembro de clase) al mismo tiempo, por lo que intenta definirse a sí mismo como una función en sí mismo.

2) ¿Es involuntario? tipo de Es una consecuencia involuntaria de un comportamiento intencionado.

Básicamente, este es un error que Microsoft reconoce pero que ha archivado como "No se Corrige", documentado en Connect connect.microsoft.com/VisualStudio/feedback/details/621384/… .

No puedo encontrar la especificación de idioma 5.0 en línea (en forma de artículo o blog) en ninguna parte, pero si está interesado, puede descargarla here . Estamos interesados ​​en la página 161, sección 7.6.4, Acceso de miembros, y es la primera sección 7.6.4.1, que es la misma sección a la que se vinculó el OP (entonces era 7.5.4.1).

El hecho de que pueda nombrar un miembro y un tipo con el mismo nombre (por ejemplo, Color) es algo que fue explícitamente permitido, aunque su identificador ahora tiene dos significados distintos. Aquí está el lenguaje de la especificación:

7.6.4.1 Nombres simples idénticos y nombres de tipos En un acceso de miembro de la forma EI, si E es un identificador único, y si el significado de E como nombre simple (§7.6.2) es una constante, campo, propiedad, variable local, o parámetro con el mismo tipo que el significado de E como nombre de tipo (§3.8), entonces se permiten ambos significados posibles de E. Los dos posibles significados de EI nunca son ambiguos, ya que necesariamente debo ser miembro del tipo E en ambos casos. En otras palabras, la regla simplemente permite el acceso a los miembros estáticos y a los tipos anidados de E donde, de otro modo, se habría producido un error en tiempo de compilación. Por ejemplo:

struct Color { public static readonly Color White = new Color(...); public static readonly Color Black = new Color(...); public Color Complement() {...} } class A { public Color Color; // Field Color of type Color void F() { Color = Color.Black; // References Color.Black static member Color = Color.Complement(); // Invokes Complement() on Color field } static void G() { Color c = Color.White; // References Color.White static member } }

Aquí está la parte clave:

ambos posibles significados de E están permitidos. Los dos posibles significados de EI nunca son ambiguos, ya que necesariamente debo ser miembro del tipo E en ambos casos. En otras palabras, la regla simplemente permite el acceso a los miembros estáticos y a los tipos anidados de E donde, de otro modo, se habría producido un error en tiempo de compilación.

Cuando define Color Color = Color.Brown , algo cambia. Como I (Brown) debe ser miembro de E (Color) en ambos casos (estático y no estático), esta regla le permite acceder a ambos, en lugar de restringir uno debido al contexto actual (no estático). Sin embargo, ahora has convertido uno de los contextos (tu no estático) en constante. Como está permitiendo ambos, intenta definir Color.Brown como enum y como miembro de la clase, pero hay un problema dependiendo del valor (no puede tener const I = I + 1 por ejemplo).

De acuerdo, entonces la Especificación del lenguaje C # tiene una sección especial (vinculada a una versión anterior) en la regla Color Color donde un miembro y su tipo tienen el mismo nombre. El famoso gurú Eric Lippert escribió una vez sobre eso.

La pregunta que voy a hacer aquí es en cierto sentido (no) lo mismo que se preguntó en el hilo Definición circular en una enumeración constante . Puedes ir y votar esa otra pregunta si quieres.

Ahora para mi pregunta. Considera este código:

namespace N { public enum Color { Green, Brown, Purple, } public class C1 { public const Color Color = Color.Brown; // error CS0110 - WHY? Compiler confused by Color Color? } public class C2 { public static readonly Color Color = Color.Brown; // fine } public class C3 { public static Color Color = Color.Brown; // fine } public class C4 { public Color Color = Color.Brown; // fine } }

El punto aquí es que en cada situación anterior, el Color identificador que está más a la derecha puede referirse al tipo de enum , o al miembro de la clase con el mismo nombre. Pero la regla Color Color mencionada anteriormente significa que deberíamos ver si el miembro ( Brown ) es estático o no estático. Dado que en este caso es estático, deberíamos interpretar Color consecuencia.

Mi principal pregunta obvia: ¿Por qué esto no funciona con un miembro de tipo const ? ¿Esto es involuntario?

(Obviamente, decir N.Color.Brown ( N es el espacio de nombres) "lo arregla", ¡no estoy preguntando sobre eso!)

Nota al pie: con const variable local, la anomalía anterior no existe:

public class C5 { public Color Color; void M() { const Color Color = Color.Brown; // works (no warning for not using local variable?) } } public class C6 { public Color Color; void M() { const Color other = Color.Brown; // works (warning CS0219, ''other'' not used) } }


Es un error. No puedo reproducir el problema en CTP 5 de VS 2015, y creo que este debería haber sido reparado como parte de la reescritura de Roslyn. Sin embargo, un comentador a continuación señala que pueden reproducirlo en CTP 6. Por lo tanto, no estoy seguro de lo que está ocurriendo aquí en cuanto a si este error se ha solucionado o no.

En una nota personal: no recuerdo específicamente si me encargaron investigar este cuando se informó por primera vez en 2010, pero dado que trabajé bastante en los detectores de circularidad de entonces, las probabilidades son bastante buenas.

Esto está lejos del único error que había en los detectores de circularidad; se confundiría bastante si hubiera tipos genéricos anidados que a su vez tuvieran tipos básicos genéricos cuyos argumentos de tipo involucraran los tipos anidados.

No me sorprende en absoluto que Alex "no arregle" este; Pasé bastante tiempo reescribiendo el código que hacía la detección de circularidad de clase y el cambio se consideró demasiado arriesgado. Todo ese trabajo fue enviado a Roslyn.

Si le interesa ver cómo funciona el código de encuadernación Color Color en Roslyn, eche un vistazo al método BindLeftOfPotentialColorColorMemberAccess , que tiene un nombre BindLeftOfPotentialColorColorMemberAccess : me encantan algunos nombres de métodos descriptivos, en Binder_Expressions.cs .


Estoy seguro de que tiene algo que ver con el hecho de que el valor de la constante debe ser determinista en tiempo de compilación, pero el valor de la propiedad (estática) se determinará en tiempo de ejecución.