C # - Uso de palabras clave virtual + anulación vs. nuevo


204

¿Cuáles son las diferencias entre declarar un método en un tipo base " virtual" y luego anularlo en un tipo secundario usando la overridepalabra clave " " en lugar de simplemente usar la newpalabra clave " " al declarar el método coincidente en el tipo secundario?


3
MSDN dice "Usar newcrea un nuevo miembro con el mismo nombre y hace que el miembro original se oculte, mientras overrideextiende la implementación para un miembro heredado"
AminM


Respuestas:


181

La palabra clave "nuevo" no anula, significa un nuevo método que no tiene nada que ver con el método de la clase base.

public class Foo
{
     public bool DoSomething() { return false; }
}

public class Bar : Foo
{
     public new bool DoSomething() { return true; }
}

public class Test
{
    public static void Main ()
    {
        Foo test = new Bar ();
        Console.WriteLine (test.DoSomething ());
    }
}

Esto imprime falso, si usó anulación, se habría impreso verdadero.

(Código base tomado de Joseph Daigle)

Entonces, si estás haciendo un verdadero polimorfismo, DEBES ANULAR SIEMPRE . El único lugar donde necesita usar "nuevo" es cuando el método no está relacionado de ninguna manera con la versión de la clase base.


Deberías revisar tu código, había hecho que los métodos fueran estáticos (lo cual es válido para usar la palabra clave "nueva"). Pero decidí que era más claro usar métodos de instancia.
Joseph Daigle

1
Gracias, me perdí la parte "estática". Debe tomar más atención en el futuro
albertein

1
Tenga en cuenta que la línea Foo test = new Bar () es crucial aquí, la palabra clave new / override para los métodos determina qué método se llama cuando coloca una barra en una variable Foo.
Thomas N

... así que es exactamente lo mismo que simplemente no tener virtualen la base y overrideen derivado? ¿Por qué existe? El código aún se ejecutará incluso sin / new¿entonces, es puramente legible?
Don Cheadle

Su respuesta, querido señor, es bastante vaga. new y virtual-override hacen lo mismo, la única diferencia es que new HIDES el método en la clase padre y override ... bueno, lo anula. Por supuesto, se imprime falso porque su objeto de prueba es del tipo Foo, si fuera del tipo Bar, se imprimiría verdadero. Ejemplo bastante complicado, sin embargo.
MrSilent

228

Siempre encuentro cosas como esta más fáciles de entender con imágenes:

Nuevamente, tomando el código de Joseph Daigle,

public class Foo
{
     public /*virtual*/ bool DoSomething() { return false; }
}

public class Bar : Foo
{
     public /*override or new*/ bool DoSomething() { return true; }
}

Si luego llama al código de esta manera:

Foo a = new Bar();
a.DoSomething();

NOTA: Lo importante es que nuestro objeto es en realidad un Bar, pero lo estamos almacenando en una variable de tipoFoo (esto es similar a lanzarlo)

Entonces el resultado será el siguiente, dependiendo de si usó virtual/ overrideo newal declarar sus clases.

Imagen de explicación virtual / de anulación


3
Gracias ... pero ¿podría explicar un poco sobre la imagen de arriba con respecto al casting que dijo?
odiseh el

¡Vaya! Si olvidé agregar estas pocas líneas a mi anterior. comentario: ¿quiere decir que virtual / anulación y no virtual / nuevo se usan solo para el concepto de polimorfismo y cuando simplemente declara una variable (sin usar conversión) no significan? Gracias de nuevo.
odiseh el

Esta respuesta es exactamente lo que estaba buscando. El único problema que tuve es que las imágenes de Flickr están bloqueadas en mi lugar de trabajo, así que tuve que acceder a ellas a través de mi teléfono ...
mezoid

77
Esfuerzo real para responder la pregunta.
iMatoria

¿Votaría si puedes arreglar la imagen? Está bloqueado detrás del firewall del cuerpo ...
Jeremy Thompson

43

Aquí hay un código para comprender la diferencia en el comportamiento de los métodos virtuales y no virtuales:

class A
{
    public void foo()
    {
        Console.WriteLine("A::foo()");
    }
    public virtual void bar()
    {
        Console.WriteLine("A::bar()");
    }
}

class B : A
{
    public new void foo()
    {
        Console.WriteLine("B::foo()");
    }
    public override void bar()
    {
        Console.WriteLine("B::bar()");
    }
}

class Program
{
    static int Main(string[] args)
    {
        B b = new B();
        A a = b;
        a.foo(); // Prints A::foo
        b.foo(); // Prints B::foo
        a.bar(); // Prints B::bar
        b.bar(); // Prints B::bar
        return 0;
    }
}

gracias por esto, pero ¿por qué usar newpara "ocultar" el método base, cuando simplemente no usar anulación parece hacer lo mismo?
Don Cheadle

1
No creo que se haya creado para evitar que se anule la clase base. Creo que fue creado para evitar el choque de nombres, ya que no puede anular un método que no lo es virtualy el compilador se quejará si ve el mismo nombre de función en una clase "línea de sangre" sin virtualfirma
mr5

19

La newpalabra clave realmente crea un miembro completamente nuevo que solo existe en ese tipo específico.

Por ejemplo

public class Foo
{
     public bool DoSomething() { return false; }
}

public class Bar : Foo
{
     public new bool DoSomething() { return true; }
}

El método existe en ambos tipos. Cuando usa la reflexión y obtiene los miembros de tipo Bar, en realidad encontrará 2 métodos llamados DoSomething()que se ven exactamente iguales. Al usarlo new, oculta efectivamente la implementación en la clase base, de modo que cuando las clases se derivan Bar(en mi ejemplo), la llamada al método base.DoSomething()va Bary no Foo.


9

virtual / override le dice al compilador que los dos métodos están relacionados y que, en algunas circunstancias, cuando crees que estás llamando al primer método (virtual), en realidad es correcto llamar al segundo método (anulado). Este es el fundamento del polimorfismo.

(new SubClass() as BaseClass).VirtualFoo()

Llamará al método anulado VirtualFoo () de la Subclase.

new le dice al compilador que está agregando un método a una clase derivada con el mismo nombre que un método en la clase base, pero que no tienen relación entre sí.

(new SubClass() as BaseClass).NewBar()

Llamará al método NewBar () de BaseClass, mientras que:

(new SubClass()).NewBar()

Llamará al método NewBar () de la Subclase.


realmente me gusta esta frase "le dice al compilador"
Mina Gabriel

"fundamento del polimorfismo" es otro refrán elegante :)
Jeremy Thompson

8

Más allá de solo los detalles técnicos, creo que el uso de virtual / override comunica mucha información semántica sobre el diseño. Cuando declara un método virtual, indica que espera que las clases de implementación quieran proporcionar sus propias implementaciones no predeterminadas. Omitir esto en una clase base, asimismo, declara la expectativa de que el método predeterminado debería ser suficiente para todas las clases de implementación. Del mismo modo, se pueden usar declaraciones abstractas para forzar la implementación de clases para proporcionar su propia implementación. Nuevamente, creo que esto comunica mucho acerca de cómo el programador espera que se use el código. Si escribiera tanto las clases básicas como las de implementación y me encontrara usando nuevas, volvería a pensar seriamente en la decisión de no hacer que el método sea virtual en los padres y declare mi intención específicamente.


Las explicaciones técnicas de new vs override son sólidas, pero esta respuesta parece ser la más útil para guiar a los desarrolladores sobre los que usar.
Sully

4

La diferencia entre la palabra clave de anulación y la nueva palabra clave es que la primera anula el método y la segunda oculta el método.

Consulte los siguientes enlaces para obtener más información ...

MSDN y otros


3
  • newLa palabra clave es para ocultar. - significa que está ocultando su método en tiempo de ejecución. La salida se basará en el método de la clase base.
  • overridepara anular. - significa que está invocando su método de clase derivada con la referencia de la clase base. La salida se basará en el método de clase derivada.

1

Mi versión de la explicación proviene del uso de propiedades para ayudar a comprender las diferencias.

overridees lo suficientemente simple, ¿verdad? El tipo subyacente anula el del padre.

newes quizás el engañoso (para mí lo fue). Con propiedades es más fácil de entender:

public class Foo
{
    public bool GetSomething => false;
}

public class Bar : Foo
{
    public new bool GetSomething => true;
}

public static void Main(string[] args)
{
    Foo foo = new Bar();
    Console.WriteLine(foo.GetSomething);

    Bar bar = new Bar();
    Console.WriteLine(bar.GetSomething);
}

Al usar un depurador puede notar que Foo footiene 2 GetSomething propiedades, ya que en realidad tiene 2 versiones de la propiedad, Foo'sy Bar', y para saber cuál usar, c # "selecciona" la propiedad para el tipo actual.

Si quisieras usar la versión de la barra, hubieras usado anular o usar Foo fooen su lugar.

Bar bartiene solo 1 , ya que quiere un comportamiento completamente nuevo para GetSomething.


0

No marcar un método con nada significa: vincular este método utilizando el tipo de compilación del objeto, no el tipo de tiempo de ejecución (enlace estático).

Marcar un método con virtualmedios: enlace este método utilizando el tipo de tiempo de ejecución del objeto, no el tipo de tiempo de compilación (enlace dinámico).

Marcar un virtualmétodo de clase base con overrideen clase derivada significa: Este es el método a vincular utilizando el tipo de tiempo de ejecución del objeto (enlace dinámico).

Marcar un virtualmétodo de clase base con newen clase derivada significa: Este es un método nuevo, que no tiene relación con el que tiene el mismo nombre en la clase base y debe vincularse usando el tipo de tiempo de compilación del objeto (enlace estático).

No marcar un virtualmétodo de clase base en la clase derivada significa: Este método está marcado como new(enlace estático).

Marcar un método abstractsignifica: Este método es virtual, pero no declararé un cuerpo para él y su clase también es abstracta (enlace dinámico).

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.