Pregunta

¿Cuáles son las diferencias entre declarar un método en un tipo base " virtual " y luego anularlo en un tipo secundario usando el " override " palabra clave en lugar de simplemente usar el " nuevo " palabra clave al declarar el método de coincidencia en el tipo de niño?

¿Fue útil?

Solución

El " nuevo " la palabra clave no se 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 se imprime como falso, si usó anularlo, se habría impreso como verdadero.

(Código base tomado de Joseph Daigle)

Por lo tanto, si está haciendo un polimorfismo real, SIEMPRE DEBERÁ ANULAR . El único lugar donde debe usar " nuevo " es cuando el método no está relacionado de ninguna manera con la versión de clase base.

Otros consejos

Siempre encuentro cosas como esta que se entienden más fácilmente con fotos:

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 llamas al código como este:

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

NOTA: Lo importante es que nuestro objeto es en realidad una Bar , pero lo almacenamos en una variable de tipo Foo (esto es similar a lanzarlo)

Luego, el resultado será el siguiente, dependiendo de si utilizó virtual / override o new al declarar sus clases.

 Imagen de explicación virtual / anulada

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;
    }
}

La palabra clave new en realidad 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 utiliza la reflexión y obtiene los miembros del tipo Bar , en realidad encontrará 2 métodos llamados DoSomething () que se ven exactamente iguales. Al usar new , se oculta la implementación en la clase base, de modo que cuando las clases derivan de Bar (en mi ejemplo), el método llama a base.DoSomething ( ) va a Bar y no a Foo .

virtual / override le dice al compilador que los dos métodos están relacionados y que en algunas circunstancias, cuando piense que está llamando al primer método (virtual), en realidad es correcto llamar al segundo (invalidado) método en su lugar. Esta es la base del polimorfismo.

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

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

nuevo 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 no tienen relación entre ellos.

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

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

(new SubClass()).NewBar()

Llamará al método NewBar () de SubClass.

Más allá de los detalles técnicos, creo que el uso de virtual / override comunica una gran cantidad de 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, igualmente, declara la expectativa de que el método predeterminado debería ser suficiente para todas las clases implementadoras. De manera similar, uno puede usar declaraciones abstractas para forzar a las clases de implementación a proporcionar su propia implementación. Una vez más, creo que esto se comunica mucho sobre cómo el programador espera que se use el código. Si escribiera las clases de base y de implementación y me encontrara usando nuevas, seriamente repensaría la decisión de no hacer que el método sea virtual en el padre y declarar mi intención específicamente.

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

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

MSDN y Otro

    La palabra clave
  • new es para ocultar. - significa que estás ocultando tu método en tiempo de ejecución. La salida se basará en el método de clase base.
  • override para anular. - significa que está invocando su método de clase derivado con la referencia de la clase base. La salida se basará en el método de clase derivado.

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

override es bastante simple, ¿verdad? El tipo subyacente anula el del padre.

new es quizás el engañoso (para mí fue). Con las 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 utilizar un depurador, puede observar que Foo foo tiene las propiedades de 2 GetSomething , ya que en realidad tiene 2 versiones de la propiedad, Foo 's y Bar ' s, y para saber cuál usar, c # " picks " La propiedad para el tipo actual.

Si quisieras usar la versión de la barra, habrías usado anular o utilizar Foo foo en su lugar.

Barra de barra solo tiene 1 , ya que quiere un comportamiento completamente nuevo para GetSomething .

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 virtual significa: vincular 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 una clase base virtual con override en clase derivada significa: Este es el método que se debe vincular utilizando el tipo de tiempo de ejecución del objeto (enlace dinámico).

Marcar una clase base virtual con new en clase derivada significa: este es un método nuevo, que no tiene relación con el que tiene el mismo nombre en la base clase y se debe enlazar utilizando el tipo de tiempo de compilación del objeto (enlace estático).

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

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

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top