La diferencia entre anulación virtual, anulación, nueva y sellada

79

Estoy muy confundido entre algunos conceptos de programación orientada a objetos: virtual, override, newy sealed override. ¿Alguien puede explicar las diferencias?

Tengo bastante claro que si se va a utilizar el método de la clase derivada, se puede utilizar la overridepalabra clave para que la clase derivada anule el método de la clase base. Pero no estoy seguro de newy sealed override.

xorpower
fuente

Respuestas:

107

La palabra clave virtual se utiliza para modificar un método, propiedad, indexador o declaración de evento, y permitir que se anule en una clase derivada. Por ejemplo, este método puede ser reemplazado por cualquier clase que lo herede: use el nuevo modificador para ocultar explícitamente un miembro heredado de una clase base. Para ocultar un miembro heredado, declárelo en la clase derivada con el mismo nombre y modifíquelo con el nuevo modificador.

Todo esto tiene que ver con el polimorfismo. Cuando se llama a un método virtual en una referencia, el tipo real de objeto al que se refiere la referencia se utiliza para decidir qué implementación de método utilizar. Cuando un método de una clase base se reemplaza en una clase derivada, se usa la versión en la clase derivada, incluso si el código de llamada no "sabía" que el objeto era una instancia de la clase derivada. Por ejemplo:

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

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

...

Base d = new Derived();
d.SomeMethod();

terminará llamando a Derived.SomeMethod si eso anula Base.SomeMethod.

Ahora, si usa la palabra clave new en lugar de anular , el método de la clase derivada no anula el método de la clase base, simplemente lo oculta. En ese caso, codifique como este:

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

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

...


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

Primero llamará Base.SomeOtherMethod, luego Derived.SomeOtherMethod. Son efectivamente dos métodos completamente separados que tienen el mismo nombre, en lugar de que el método derivado anule el método base.

Si no especifica new o overrides, la salida resultante es la misma que si hubiera especificado new, pero también recibirá una advertencia del compilador (ya que es posible que no sepa que está ocultando un método en la clase base método, o de hecho puede haber querido anularlo, y simplemente se olvidó de incluir la palabra clave).

Una declaración de propiedad primordial puede incluir el modificador sellado . El uso de este modificador evita que una clase derivada anule aún más la propiedad. Los accesos de una propiedad sellada también están sellados.

CharithJ
fuente
gracias por la entrada ... pero una cosa que no me viene a la mente es ... ¿cuál es el uso de Base b = new Derived ()? ¿Es este objeto de creación de clase base o clase derivada?
xorpower
2
Clase derivada. Creo que debes investigar más sobre el polimorfismo. Aquí hay uno bueno para su lectura. msdn.microsoft.com/en-us/library/ms173152(v=vs.80).aspx
CharithJ
5
@Xor: En ese caso, está creando una instancia de un Derivedobjeto y almacenando la referencia en una Basevariable. Esto es válido porque un Derivedobjeto también es un Baseobjeto. Eso es como decir que necesitamos una "persona" para que tengamos a "Johnny" que resulta ser una persona. Mismo trato aquí.
Jeff Mercado
Quiero agregar solo un punto aquí. Base b = new Derived()establece que Basese puede acceder a una clase a través de una Derived classreferencia porque derived classes una especialización de su clase base. Derivedlas clases pueden realizar todas las operaciones (por ejemplo, invocar métodos de clase base, etc. ) que base classpuede hacer. Pero Base classno puede realizar las operaciones que Derived classpuede hacer. Entonces Derived d = new Base()no es correcto pero Base b = new Derived()es correcto.
mmushtaq
¿Puede aclarar el propósito de usar el newmodificador para hide a base class method? En el segundo ejemplo, la llamada b.SomeOtherMethod()invoca la implementación de la clase base (se podría decir que ha ocultado el método de la clase derivada). Si ese es un ejemplo típico de uso, entonces newparece que se usa cuando la persona que llama tiene la intención de tener una variable de a compile-time typepara usar su método, y no el método de cualquiera runtime typesque se le pueda asignar.
Minh Tran
35

Cualquier método puede ser reemplazable (= virtual) o no. La decisión la toma quien define el método:

class Person
{
    // this one is not overridable (not virtual)
    public String GetPersonType()
    {
        return "person";
    }

    // this one is overridable (virtual)
    public virtual String GetName()
    {
        return "generic name";
    }
}

Ahora puede anular los métodos que se pueden anular:

class Friend : Person
{
    public Friend() : this("generic name") { }

    public Friend(String name)
    {
        this._name = name;
    }

    // override Person.GetName:
    public override String GetName()
    {
        return _name;
    }
}

Pero no puede anular el GetPersonTypemétodo porque no es virtual.

Creemos dos instancias de esas clases:

Person person = new Person();
Friend friend = new Friend("Onotole");

Cuando GetPersonTypese llama a un método no virtual por Fiendinstancia, en realidad Person.GetPersonTypese llama:

Console.WriteLine(friend.GetPersonType()); // "person"

Cuando GetNamese llama al método virtual por Friendinstancia, Friend.GetNamese llama:

Console.WriteLine(friend.GetName()); // "Onotole"

Cuando GetNamese llama al método virtual por Personinstancia, Person.GetNamese llama:

Console.WriteLine(person.GetName()); // "generic name"

Cuando se llama a un método no virtual, no se busca el cuerpo del método; el compilador ya conoce el método real que debe llamarse. Mientras que con los métodos virtuales, el compilador no puede estar seguro de cuál llamar, y se busca en el tiempo de ejecución en la jerarquía de clases de abajo hacia arriba, comenzando en el tipo de instancia en la que se llama al método: porque friend.GetNameparece que comienza en la Friendclase y lo encuentra de inmediato, para la person.GetNameclase en que comienza Persony lo encuentra allí.

A veces crea una subclase, anula un método virtual y no desea más anulaciones hacia abajo en la jerarquía; lo usa sealed overridepara eso (diciendo que es el último que anula el método):

class Mike : Friend
{
    public sealed override String GetName()
    {
        return "Mike";
    }
}

Pero a veces tu amigo Mike decide cambiar su género y, por lo tanto, su nombre a Alice :) Podrías cambiar el código original o subclase Mike:

class Alice : Mike
{
    public new String GetName()
    {
        return "Alice";
    }
}

Aquí crea un método completamente diferente con el mismo nombre (ahora tiene dos). ¿Qué método y cuándo se llama? Depende de cómo lo llames:

Alice alice = new Alice();
Console.WriteLine(alice.GetName());             // the new method is called, printing "Alice"
Console.WriteLine(((Mike)alice).GetName());     // the method hidden by new is called, printing "Mike"

Cuando lo llamas desde Alicela perspectiva de, llamas Alice.GetName, cuando desde Mike, llamas Mike.GetName. Aquí no se realiza ninguna búsqueda en tiempo de ejecución, ya que ambos métodos no son virtuales.

Siempre puede crear newmétodos, ya sea que los métodos que está ocultando sean virtuales o no.

Esto también se aplica a las propiedades y eventos: se representan como métodos debajo.

Loki Kriasus
fuente
1
No hay una respuesta simple y completa que esta que encontré en ninguna parte. Gracias Loki
Reevs
19

De forma predeterminada, un método no se puede anular en una clase derivada a menos que se declare virtual, o abstract. virtualsignifica verificar implementaciones más nuevas antes de llamar y abstractsignifica lo mismo, pero se garantiza que se anulará en todas las clases derivadas. Además, no se necesita implementación en la clase base porque se volverá a definir en otro lugar.

La excepción a lo anterior es el newmodificador. Un método no declarado virtualo abstractpuede redefinirse con el newmodificador en una clase derivada. Cuando se llama al método en la clase base, se ejecuta el método base, y cuando se llama en la clase derivada, se ejecuta el nuevo método. Todo lo que las newpalabras clave le permiten hacer es tener dos métodos con el mismo nombre en una jerarquía de clases.

Finalmente, un sealedmodificador rompe la cadena de virtualmétodos y los hace no anulables nuevamente. Esto no se usa con frecuencia, pero la opción está ahí. Tiene más sentido con una cadena de 3 clases cada una derivada de la anterior

A -> B -> C

si Atiene un método virtualo abstract, es decir , overriddenen B, entonces también puede evitar Ccambiarlo nuevamente declarándolo sealeden B.

sealedtambién se usa en classes, y ahí es donde comúnmente encontrará esta palabra clave.

Espero que esto ayude.

John Alexiou
fuente
8
 public class Base 
 {
   public virtual void SomeMethod()
   {
     Console.WriteLine("B");
   }
  }

public class Derived : Base
{
   //Same method is written 3 times with different keywords to explain different behaviors. 


   //This one is Simple method
  public void SomeMethod()
  {
     Console.WriteLine("D");
  }

  //This method has 'new' keyword
  public new void SomeMethod()
  {
     Console.WriteLine("D");
  }

  //This method has 'override' keyword
  public override void SomeMethod()
  {
     Console.WriteLine("D");
  }
}

Ahora lo primero es lo primero

 Base b=new Base();
 Derived d=new Derived();
 b.SomeMethod(); //will always write B
 d.SomeMethod(); //will always write D

Ahora las palabras clave tienen que ver con el polimorfismo.

 Base b = new Derived();
  1. Usar virtualen la clase base y anular en Deriveddará D (polimorfismo).
  2. Usar overridesin virtualin Basedará error.
  3. De manera similar, escribir un método (sin anulación) con virtualescribirá 'B' con una advertencia (porque no se realiza ningún polimorfismo).
  4. Para ocultar dicha advertencia como en el punto anterior, escriba newantes de ese método simple en Derived.
  5. new La palabra clave es otra historia, simplemente oculta la advertencia que dice que la propiedad con el mismo nombre está en la clase base.
  6. virtualo newambos son iguales excepto el nuevo modificador

  7. newy overrideno se puede utilizar antes del mismo método o propiedad.

  8. sealed antes de que cualquier clase o método lo bloquee para usarlo en la clase derivada y da un error de tiempo de compilación.
Charlie
fuente
Lo siento, pero -1 debido a múltiples errores de compilación: método declarado varias veces con los mismos parámetros, sin comillas alrededor de las cadenas B y D ...
DeveloperDan