c# - usar - typeof(T) dentro de los tipos genéricos anidados
gettype() and typeof (3)
No entiendo por qué lo siguiente se comporta como lo hace en absoluto. Ni siquiera sé si es por esconderse o algo más.
class A<T>
{
public class B : A<int>
{
public void b()
{
Console.WriteLine(typeof(T).ToString());
}
public class C : B
{
public void c()
{
Console.WriteLine(typeof(T).ToString());
}
}
public class D : A<T>.B
{
public void d()
{
Console.WriteLine(typeof(T).ToString());
}
}
}
}
class Program
{
static void Main(string[] args)
{
A<string>.B.C c = new A<string>.B.C();
A<string>.B.D d = new A<string>.B.D();
c.c();
c.b();
d.d();
d.b();
}
}
Las preguntas son:
¿Por qué
cc()
produce System.String mientras quecb()
produceSystem.Int32
?¿Por qué
dd()
ydb()
producenSystem.String
y no se comportan exactamente de la misma manera que la claseC
?
Esta es una variación de un rompecabezas que publiqué en mi blog hace muchos años:
blogs.msdn.com/b/ericlippert/archive/2007/07/27/…
y Cyrus publicó en su blog antes de eso:
http://blogs.msdn.com/b/cyrusn/archive/2005/08/01/446431.aspx
Vea la discusión allí para más detalles.
Brevemente: ¿qué significa B
en la class C : B
? Revise el contenedor, class B
¿Contiene algún tipo llamado B
? No. Entonces revisa la clase base del contenedor. La clase base del contenedor es A<int>
. ¿Contiene algo llamado B
? Sí. Entonces esto significa class C : A<int>.B
.
Ahora decimos que c
es A<string>.BC
. Llamamos al método A<string>.BCc()
¿Qué es T
largo de A<string>
? Obviamente la string
. Entonces cc()
imprime String
para T
Ahora llamamos A<string>.BCb()
pero no hay tal método en A<string>.BC
directamente. ¿De dónde saca este método? Desde su clase base. ¿Cuál es su clase base? A<int>.B
. Entonces llamamos A<int>.Bb()
. ¿Qué es T
largo de A<int>
? Obviamente int
.
Ahora llegamos a A<string>.BDd()
. La clase base es irrelevante. T
es string
largo de A<string>
.
Y finalmente A<string>.BDb()
. No hay tal método en A<string>.BD
directamente, por lo que debe obtenerlo de su tipo base. T
es una string
en A<string>
, por lo que el tipo base es A<string>.B
. Por lo tanto, esto llama A<string>.Bb()
.
Si eso no tiene sentido para ti, explica todo. Vamos a sustituir String por T:
class A_string
{
public class B : A_int
{
public void b()
{
Console.WriteLine(typeof(string).ToString());
}
public class C : A_int.B // Note!
{
public void c()
{
Console.WriteLine(typeof(string).ToString());
}
}
public class D : A_string.B
{
public void d()
{
Console.WriteLine(typeof(string).ToString());
}
}
}
}
OK, ese es uno de los tipos. Ahora hagamos lo mismo para int:
class A_int
{
public class B : A_int
{
public void b()
{
Console.WriteLine(typeof(int).ToString());
}
public class C : A_int.B // Note!
{
public void c()
{
Console.WriteLine(typeof(int).ToString());
}
}
public class D : A_int.B
{
public void d()
{
Console.WriteLine(typeof(int).ToString());
}
}
}
}
Ahora, dados esos tipos, debería estar claro qué A_string.BCc()
, A_string.BCb()
, etc., todos imprimen.
Este es un ejemplo un poco más complejo de un rompecabezas que Eric Lippert describe en blogs.msdn.com/b/ericlippert/archive/2007/07/27/… , que luego se explica en este artículo .
El breve resumen (recomiendo leer el artículo) es que en este caso hay una ambigüedad en la línea C : B
Se debe tomar una decisión sobre lo que B
realmente significa en este contexto; ya sea A<T>.B
o A<int>.B
. Esencialmente, el compilador elige este último, dado un criterio particular de "betterness" descrito en la especificación.
A<string>.BC
hereda A<int>.B
, porque B
en la declaración de la clase base proviene primero del ámbito primario interno. (para aclarar, su ámbito principal es A<T>.B
, que contiene un tipo llamado B
refiere a A<int>.B
, heredado de su clase base A<int>
)
La llamada b()
proviene de su clase base, en la que T
(del ámbito principal) es int
.
D
explícitamente hereda A<T>.B
, usando T
desde el alcance más externo ( A<T>
), por lo que su T
siempre proviene de A<>
en su nombre de tipo.