c# - namespace - Este es Sparta, ¿o sí?
namespace c# que es (3)
Pero, ¿por qué
Sparta
enMakeItReturnFalse()
refiere a{namespace}.Place.Sparta
lugar de{namespace}.Sparta
?
Básicamente, porque eso es lo que dicen las reglas de búsqueda de nombres. En la especificación C # 5, las reglas de nomenclatura relevantes se encuentran en la sección 3.8 ("Espacio de nombres y nombres de tipo").
Los primeros dos puntos, truncados y anotados, se leen:
- Si el nombre del espacio de nombres o tipos es de la forma
I
o de la formaI<A1, ..., AK>
[entonces K = 0 en nuestro caso] :
- Si K es cero y el nombre del espacio de nombres o tipos aparece dentro de una declaración de método genérico [no, no hay métodos genéricos]
- De lo contrario, si el espacio de nombre o nombre de tipo aparece dentro de una declaración de tipo, entonces para cada tipo de instancia T (§10.3.1), comenzando con el tipo de instancia de esa declaración de tipo y continuando con el tipo de instancia de cada clase adjunta o declaración de estructura (si existe):
- Si
K
es cero y la declaración deT
incluye un parámetro de tipo con el nombreI
, entonces el espacio de nombres o nombre de tipo se refiere a ese parámetro de tipo. [No]- De lo contrario, si el nombre de espacio de nombres o tipos aparece dentro del cuerpo de la declaración de tipo, y
T
o cualquiera de sus tipos base contienen un tipo accesible anidado que tiene parámetros de tipo de nombreI
yK
, entonces el nombre de espacio de nombre o tipo se refiere a ese tipo construido con los argumentos de tipo dados. [¡Bingo!]- Si los pasos anteriores no tuvieron éxito, entonces, para cada espacio de nombres
N
, comenzando con el espacio de nombres en el que se produce el espacio de nombres o nombres de tipos, continuando con cada espacio de nombres adjunto (si corresponde) y terminando con el espacio de nombres global, los siguientes pasos son evaluado hasta que se localice una entidad:
- Si
K
es cero eI
es el nombre de un espacio de nombres enN
, entonces ... [Sí, eso tendría éxito]
Entonces, ese último punto es lo que recoge la
clase
Sparta
si el primer punto no encuentra nada ... pero cuando la clase base
Place
define una interfaz
Sparta
, se encuentra
antes de
considerar la clase
Sparta
.
Tenga en cuenta que si hace que el tipo anidado
Place.Sparta
una clase en lugar de una interfaz, aún se compila y devuelve
false
, pero el compilador emite una advertencia porque sabe que una instancia de
Sparta
nunca será una instancia de la clase
Place.Sparta
.
Del mismo modo, si mantiene
Place.Sparta
una interfaz pero
sealed
clase
Sparta
, recibirá una advertencia porque ninguna instancia de
Sparta
podría implementar la interfaz.
La siguiente es una pregunta de entrevista. Se me ocurrió una solución, pero no estoy seguro de por qué funciona.
Pregunta:
Sin modificar la clase
Sparta
, escriba un código que haga que
MakeItReturnFalse
devuelva
false
.
public class Sparta : Place
{
public bool MakeItReturnFalse()
{
return this is Sparta;
}
}
Mi solución: (SPOILER)
public class Place
{
public interface Sparta { }
}
Pero, ¿por qué
Sparta
en
MakeItReturnFalse()
refiere a
{namespace}.Place.Sparta
lugar de
{namespace}.Sparta
?
Al resolver un nombre a su valor, la "cercanía" de la definición se utiliza para resolver ambigüedades. Cualquier definición que sea "más cercana" es la que se elige.
La interfaz
Sparta
se define dentro de una clase base.
La clase
Sparta
se define en el espacio de nombres que contiene.
Las cosas definidas dentro de una clase base están "más cerca" que las cosas definidas en el mismo espacio de nombres.
Hermosa pregunta! Me gustaría agregar una explicación un poco más larga para aquellos que no hacen C # a diario ... porque la pregunta es un buen recordatorio de los problemas de resolución de nombres en general.
Tome el código original, ligeramente modificado de las siguientes maneras:
-
Imprimamos los nombres de los tipos en lugar de compararlos como en la expresión original (es decir,
return this is Sparta
). -
Definamos la interfaz
Athena
en la superclasePlace
para ilustrar la resolución del nombre de la interfaz. -
Imprimamos también el nombre del tipo de
this
ya que está vinculado en la claseSparta
, solo para aclararlo todo.
El código se ve así:
public class Place {
public interface Athena { }
}
public class Sparta : Place
{
public void printTypeOfThis()
{
Console.WriteLine (this.GetType().Name);
}
public void printTypeOfSparta()
{
Console.WriteLine (typeof(Sparta));
}
public void printTypeOfAthena()
{
Console.WriteLine (typeof(Athena));
}
}
Ahora creamos un objeto
Sparta
y llamamos a los tres métodos.
public static void Main(string[] args)
{
Sparta s = new Sparta();
s.printTypeOfThis();
s.printTypeOfSparta();
s.printTypeOfAthena();
}
}
El resultado que obtenemos es:
Sparta
Athena
Place+Athena
Sin embargo, si modificamos la clase Place y definimos la interfaz Sparta:
public class Place {
public interface Athena { }
public interface Sparta { }
}
entonces es esta
Sparta
, la interfaz, la que estará disponible primero para el mecanismo de búsqueda de nombres y la salida de nuestro código cambiará a:
Sparta
Place+Sparta
Place+Athena
Por lo tanto, hemos estropeado efectivamente la comparación de tipos en la definición de la función
MakeItReturnFalse
simplemente definiendo la interfaz Sparta en la superclase, que se encuentra primero por la resolución del nombre.
Pero, ¿por qué C # eligió priorizar las interfaces definidas en la superclase en la resolución de nombres? @JonSkeet lo sabe! Y si lee su respuesta, obtendrá los detalles del protocolo de resolución de nombres en C #.