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í?

Author: John Saunders, 2010-06-02

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"); 
    } 
} 
 27
Author: LBushkin,
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..

 5
Author: Fredrik Mörk,
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

 1
Author: Rowland Shaw,
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
 0
Author: Matthew Whited,
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