Diferencia entre nuevo y anulación


198

Preguntándose cuál es la diferencia entre lo siguiente:

Caso 1: Clase base

public void DoIt();

Caso 1: clase heredada

public new void DoIt();

Caso 2: Clase Base

public virtual void DoIt();

Caso 2: clase heredada

public override void DoIt();

Los casos 1 y 2 parecen tener el mismo efecto según las pruebas que he realizado. ¿Hay alguna diferencia o una forma preferida?


2
Duplicado de muchas preguntas, incluyendo stackoverflow.com/questions/159978/…
Jon Skeet

Respuestas:


267

El modificador de anulación puede usarse en métodos virtuales y debe usarse en métodos abstractos. Esto indica que el compilador use la última implementación definida de un método. Incluso si se llama al método en una referencia a la clase base, usará la implementación que lo anula.

public class Base
{
    public virtual void DoIt()
    {
    }
}

public class Derived : Base
{
    public override void DoIt()
    {
    }
}

Base b = new Derived();
b.DoIt();                      // Calls Derived.DoIt

llamará Derived.DoItsi eso anula Base.DoIt.

El nuevo modificador indica al compilador que use la implementación de la clase secundaria en lugar de la implementación de la clase primaria. Cualquier código que no haga referencia a su clase pero la clase principal utilizará la implementación de la clase principal.

public class Base
{
    public virtual void DoIt()
    {
    }
}

public class Derived : Base
{
    public new void DoIt()
    {
    }
}

Base b = new Derived();
Derived d = new Derived();

b.DoIt();                      // Calls Base.DoIt
d.DoIt();                      // Calls Derived.DoIt

Primero llamará Base.DoIt, luegoDerived.DoIt . En realidad, son dos métodos completamente separados que tienen el mismo nombre, en lugar de que el método derivado anule el método base.

Fuente: blog de Microsoft


55
This indicates for the compiler to use the last defined implementation of a method. ¿Cómo puede encontrar la última implementación definida de un método?
AminM

55
Comience desde una clase concreta, verifique si tiene una implementación del método de interés. Si es así, ya está. Si no es así, vaya un paso más allá en la jerarquía de herencia, es decir, verifique si la superclase tiene el método de interés. Continúe hasta que haya encontrado el método de interés.
csoltenborn

2
También tenga en cuenta que solo puede overrideun método cuando la clase base define el método como virtual. La palabra virtuales la clase base diciendo "Hey, cuando llamo a este método, podría haber prácticamente ha sustituido por una aplicación derivada, por lo que no se sabe muy bien de antemano qué método de aplicación en realidad estoy llamando en tiempo de ejecución. Así que virtualsignifica es un marcador de posición para un método. Esto implica que los métodos que no están marcados como virtualno se pueden anular. Pero puede reemplazar cualquier método no virtual en una clase derivada con el modificador new, solo accesible a nivel derivado.
Erik Bongers

177

virtual : indica que un heredero puede anular un método

anular : anula la funcionalidad de un método virtual en una clase base, proporcionando una funcionalidad diferente.

nuevo : oculta el método original (que no tiene que ser virtual), proporcionando una funcionalidad diferente. Esto solo debe usarse donde sea absolutamente necesario.

Cuando oculta un método, aún puede acceder al método original subiendo a la clase base. Esto es útil en algunos escenarios, pero peligroso.


2
¿Por qué es peligroso lanzar un método que oculta el método base? ¿O estás insinuando que lanzar en general es peligroso?
Mark

3
@ Mark: una persona que llama puede no estar al tanto de la implementación, causando un mal uso accidental.
Jon B

¿Se puede usar overridey / o newsin virtualel método padre?
Aaron Franke

16

En el primer caso, está ocultando la definición en la clase principal. Esto significa que solo se invocará cuando se trate con el objeto como la clase secundaria. Si convierte la clase a su tipo principal, se invocará el método principal. En la segunda instancia, el método se anula y se invocará independientemente de si el objeto se convierte en la clase secundaria o primaria.


7

intente lo siguiente: (caso1)

((BaseClass)(new InheritedClass())).DoIt()

Editar: virtual + anulación se resuelve en tiempo de ejecución (por lo que anular realmente anula los métodos virtuales), mientras que nuevo simplemente crea un nuevo método con el mismo nombre y oculta el antiguo, se resuelve en tiempo de compilación -> su compilador llamará al método ' ve '


3

En el caso 1, si usó, llame al método DoIt () de la clase heredada mientras el tipo se declara como la clase base, verá incluso la acción de la clase base.

/* Results
Class1
Base1
Class2
Class2
*/
public abstract class Base1
{
    public void DoIt() { Console.WriteLine("Base1"); }
}
public  class Class1 : Base1 
{
    public new void DoIt() { Console.WriteLine("Class1"); }
}
public abstract class Base2
{
    public virtual void DoIt() { Console.WriteLine("Base2"); }
}
public class Class2 : Base2
{
    public override void DoIt() { Console.WriteLine("Class2"); }
}
static void Main(string[] args)
{
    var c1 = new Class1();
    c1.DoIt();
    ((Base1)c1).DoIt();

    var c2 = new Class2();
    c2.DoIt();
    ((Base2)c2).DoIt();
    Console.Read();
}

¿Podría publicar la advertencia o error que está recibiendo? Este código funcionó bien cuando lo publiqué originalmente.
Matthew Whited

Todo esto debe pegarse dentro de su clase de punto de entrada (Programa). Eso se eliminó para permitir un mejor formato en este sitio.
Matthew Whited

3

La diferencia entre los dos casos es que en el caso 1, el DoItmétodo base no se anula, solo se oculta. Lo que esto significa es que dependiendo del tipo de la variable depende de qué método se llamará. Por ejemplo:

BaseClass instance1 = new SubClass();
instance1.DoIt(); // Calls base class DoIt method

SubClass instance2 = new SubClass();
instance2.DoIt(); // Calls sub class DoIt method

Esto puede ser realmente confuso y resulta en un comportamiento no esperado y debe evitarse si es posible. Entonces, la forma preferida sería el caso 2.


3
  • newsignifica respetar su tipo de REFERENCIA (lado izquierdo de =), ejecutando así el método de los tipos de referencia. Si el método redefinido no tiene newpalabra clave, se comporta como lo ha hecho. Además, también se conoce como herencia no polimórfica . Es decir, "Estoy creando un método completamente nuevo en la clase derivada que no tiene absolutamente nada que ver con ningún método con el mismo nombre en la clase base". - por dicho Whitaker
  • override, que debe usarse con la virtualpalabra clave en su clase base, significa respetar su tipo de OBJETO (lado derecho de =), ejecutando así el método reemplazado independientemente del tipo de referencia. Por otra parte, también se conoce como herencia polimórfica .

Mi manera de tener en cuenta ambas palabras clave que son opuestas entre sí.

override: la virtualpalabra clave debe definirse para anular el método. El método que utiliza la overridepalabra clave que, independientemente del tipo de referencia (referencia de la clase base o clase derivada) si se instancia con la clase base, se ejecuta el método de la clase base. De lo contrario, se ejecuta el método de clase derivada.

new: si la palabra clave es utilizada por un método, a diferencia de la overridepalabra clave, el tipo de referencia es importante. Si se instancia con clase derivada y el tipo de referencia es clase base, se ejecuta el método de clase base. Si se instancia con clase derivada y el tipo de referencia es clase derivada, se ejecuta el método de clase derivada. Es decir, es contraste de overridepalabra clave. En resumen, si olvida u omite agregar una nueva palabra clave al método, el compilador se comporta de manera predeterminada a medida que newse usa la palabra clave.

class A 
{
    public string Foo() 
    {
        return "A";
    }

    public virtual string Test()
    {
        return "base test";
    }
}

class B: A
{
    public new string Foo() 
    {
        return "B";
    }
}

class C: B 
{
    public string Foo() 
    {
        return "C";
    }

    public override string Test() {
        return "derived test";
    }
}

Llamar en principal:

A AClass = new B();
Console.WriteLine(AClass.Foo());
B BClass = new B();
Console.WriteLine(BClass.Foo());
B BClassWithC = new C();
Console.WriteLine(BClassWithC.Foo());

Console.WriteLine(AClass.Test());
Console.WriteLine(BClassWithC.Test());

Salida:

A
B
B
base test
derived test

Nuevo código de ejemplo,

Juega con el código comentando uno por uno.

class X
{
    protected internal /*virtual*/ void Method()
    {
        WriteLine("X");
    }
}
class Y : X
{
    protected internal /*override*/ void Method()
    {
        base.Method();
        WriteLine("Y");
    }
}
class Z : Y
{
    protected internal /*override*/ void Method()
    {
        base.Method();
        WriteLine("Z");
    }
}

class Programxyz
{
    private static void Main(string[] args)
    {
        X v = new Z();
        //Y v = new Z();
        //Z v = new Z();
        v.Method();
}

1

Si overridese usa la palabra clave en la clase derivada, se anula el método principal.

Si newse utiliza la palabra clave en la clase derivada, el método derivado ocultado por el método principal.


1

Tenía la misma pregunta y es realmente confuso, debería considerar que la anulación y las nuevas palabras clave solo funcionan con objetos de tipo de clase base y valor de clase derivada. En este caso, sólo podrás ver el efecto de anulación y nueva: Así que si usted tiene class Ay B, Bhereda de A, a continuación, se instancia un objeto como éste:

A a = new B();

Ahora, al llamar a los métodos, se tendrá en cuenta su estado. Override : significa que extiende la función del método, luego usa el método en la clase derivada, mientras que new le dice al compilador que oculte el método en la clase derivada y use el método en la clase base. Aquí hay una muy buena vista de ese tema:

https://msdn.microsoft.com/EN-US/library/ms173153%28v=VS.140,d=hv.2%29.aspx?f=255&MSPPError=-2147217396


1

El siguiente artículo está en vb.net, pero creo que la explicación acerca de las anulaciones de nuevos vs es muy fácil de entender.

https://www.codeproject.com/articles/17477/the-dark-shadow-of-overrides

En algún momento del artículo, está esta oración:

En general, Shadows asume que se invoca la función asociada con el tipo, mientras que Overrides asume que se ejecuta la implementación del objeto.

La respuesta aceptada a esta pregunta es perfecta, pero creo que este artículo proporciona buenos ejemplos para agregar un mejor significado sobre las diferencias entre estas dos palabras clave.


1

De todos esos, nuevo es el más confuso. A través de la experimentación, la nueva palabra clave es como dar a los desarrolladores la opción de anular la implementación de la clase heredada con la implementación de la clase base definiendo explícitamente el tipo. Es como pensar al revés.

En el siguiente ejemplo, el resultado devolverá "Resultado derivado" hasta que el tipo se defina explícitamente como prueba BaseClass, solo entonces se devolverá "Resultado base".

class Program
{
    static void Main(string[] args)
    {
        var test = new DerivedClass();
        var result = test.DoSomething();
    }
}

class BaseClass
{
    public virtual string DoSomething()
    {
        return "Base result";
    }
}

class DerivedClass : BaseClass
{
    public new string DoSomething()
    {
        return "Derived result";
    }
}

3
Agrega tu comentario si te opones. Golpear y correr es tan cobarde.
útilBee

0

La diferencia funcional no se mostrará en estas pruebas:

BaseClass bc = new BaseClass();

bc.DoIt();

DerivedClass dc = new DerivedClass();

dc.ShowIt();

En este ejemplo, el Doit que se llama es el que espera que se llame.

Para ver la diferencia tienes que hacer esto:

BaseClass obj = new DerivedClass();

obj.DoIt();

Verá si se ejecuta la prueba de que en el caso 1 (como se la definió), el DoIt()en BaseClassse llama, en el caso 2 (como se la definió), el DoIt()en DerivedClassse llama.


-1

En el primer caso, llamará al método DoIt () de clase derivada porque la nueva palabra clave oculta el método DoIt () de clase base.

En el segundo caso, llamará a anulación DoIt ()

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

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

public class C : A
{
    public override void DoIt()
    {
        Console.WriteLine("C::DoIt()");
    }
}

dejemos crear instancia de estas clases

   A instanceA = new A();

    B instanceB = new B();
    C instanceC = new C();

    instanceA.DoIt(); //A::DoIt()
    instanceB.DoIt(); //B::DoIt()
    instanceC.DoIt(); //B::DoIt()

Todo se espera en lo anterior. Deje establecer instanciaB e instanciaC en instanciaA y llame al método DoIt () y verifique el resultado.

    instanceA = instanceB;
    instanceA.DoIt(); //A::DoIt() calls DoIt method in class A

    instanceA = instanceC;
    instanceA.DoIt();//C::DoIt() calls DoIt method in class C because it was overriden in class C

instanciaC.DoIt (); te daría C :: DoIt (), no B :: DoIt ()
BYS2

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.