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.Stringy 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.