Confundido acerca de "anular" vs. "nuevo" en C#
Tengo las siguientes clases:
class Base
{
public virtual void Print()
{
Console.WriteLine("Base");
}
}
class Der1 : Base
{
public new virtual void Print()
{
Console.WriteLine("Der1");
}
}
class Der2 : Der1
{
public override void Print()
{
Console.WriteLine("Der2");
}
}
Este es mi método principal:
Base b = new Der2();
Der1 d1 = new Der2();
Der2 d2 = new Der2();
b.Print();
d1.Print();
d2.Print();
La salida es Base
, Der2
, Der2
.
Por lo que sé, Override no permitirá que se ejecute el método anterior, incluso si el puntero está apuntando a ellos. Así que la primera línea debe salir Der2
también. Sin embargo Base
salió.
¿Cómo es posible? ¿Cómo la anulación no funcionó allí?
4 answers
En realidad nunca has anulado la versión Base
de Print()
. Solo lo has ocultado con un método virtual separado (llamado el mismo) en Der1
.
Cuando usas la palabra clave new
en la firma de un método, le estás diciendo al compilador que este es un método que tiene el mismo nombre que un método de una de tus clases base, pero no tiene otra relación. Puede hacer que este nuevo método sea virtual (como lo ha hecho), pero eso no es lo mismo que anular la clase base método.
En Der2
cuando anula Print
en realidad está anulando la versión 'nueva' que declaró en Der1
- no la versión es Base
. Eric Lippert tiene una excelente respuesta a una pregunta ligeramente diferente que puede ayudarlo a razonar sobre cómo se tratan los métodos virtuales en el lenguaje C#.
En su ejemplo, cuando llama a Print
, lo está llamando en el primer caso a través de una referencia de tipo Base
, por lo que la versión oculta (pero no anulada) de Print
es called. Las otras dos llamadas se envían a la implementación de Der1
, porque en este caso, realmente ha sobreescrito el método.
Puede leer más sobre esto en la documentación de MSDN de new and override.
Lo que puede haber intentado hacer con Der1 (como lo hizo con Der2) es usar la palabra clave override
:
class Base
{
public virtual void Print()
{
Console.WriteLine("Base");
}
}
class Der1 : Base
{
// omitting 'new' and using override here will override Base
public override void Print()
{
Console.WriteLine("Der1");
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:34:04
Porque Der1
no reemplazar Print
, lo reemplaza con un nuevo método que pasa a tener el mismo nombre (esto es causado por el uso de la new
palabra clave). Por lo tanto, cuando el objeto es lanzado a Base
llama a Print
en Base
; no hay sobreescritura para llamar..
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-06-01 20:03:47
override
reemplazará el método anterior, pero como su clase Der1
no anula Print()
(la sombrea, para usar un VB-ism), entonces se llama a la versión más anulada de Base
's Print()
, que resulta ser la versión que define
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-06-01 20:05:33
Como todos han dicho, la clase Der1
está reemplazando a Print()
en lugar de sobrescribirla. Para ver esto en acción, puedes basar d1
y d2
en Base
y luego llamar al método print. Luego volverá Base
.
((Base)d2).Print(); //Base
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-06-01 20:07:58