¿Diferencia entre sombreado y anulación en C #?

100

¿Cuál es la diferencia entre sombrear y anular un método en C #?

Jox
fuente

Respuestas:

152

Bueno herencia ...

suponga que tiene estas clases:

class A {
   public int Foo(){ return 5;}
   public virtual int Bar(){return 5;}
}
class B : A{
   public new int Foo() { return 1;}     //shadow
   public override int Bar() {return 1;} //override
}

entonces cuando llames a esto:

A clA = new A();
B clB = new B();

Console.WriteLine(clA.Foo()); // output 5
Console.WriteLine(clA.Bar()); // output 5
Console.WriteLine(clB.Foo()); // output 1
Console.WriteLine(clB.Bar()); // output 1

//now let's cast B to an A class
Console.WriteLine(((A)clB).Foo()); // output 5 <<<-- shadow
Console.WriteLine(((A)clB).Bar()); // output 1

Suponga que tiene una clase base y usa la clase base en todo su código en lugar de las clases heredadas, y usa shadow, devolverá los valores que devuelve la clase base en lugar de seguir el árbol de herencia del tipo real del objeto.

Ejecute el código aquí

Espero tener sentido :)

Stormenet
fuente
16
La anulación proporciona polimorfismo, el sombreado proporciona una implementación diferente en ese nivel de la jerarquía, pero no polimórficamente.
David Rodríguez - dribeas
@dribeas, ¿Qué definición de polimorfismo estarías usando para sacar esa conclusión?
AnthonyWJones
9
@AnthonyWJones, el polimorfismo es la capacidad de un tipo (derivado) para ser utilizado como un tipo (base) diferente donde la implementación real (comportamiento) es la del tipo específico real (derivado). Si anula, se llamará al método derivado a través de la referencia a la base, pero si
sombrea
2
Me parece que tiene un error en su código de muestra. El casting debería ser ((A) clB) .Foo (), ¿no?
trendl
1
No, porque la barra es virtual, por lo que aún llamará a la barra B
Stormenet
32

El sombreado es en realidad el lenguaje de VB para lo que llamaríamos ocultar en C #.

A menudo, la ocultación (sombreado en VB) y la anulación se muestran como en la respuesta de Stormenet .

Se muestra que un método virtual es anulado por una subclase y las llamadas a ese método incluso en el tipo de superclase o desde el código interno de la superclase llamarán a la implementación de reemplazo desde la subclase.

Luego, se muestra un método concreto (uno no marcado como virtual o abstracto) que se oculta mediante el uso de la newpalabra clave al definir un método con una firma idéntica en la subclase. En este caso, cuando se llama al método en el tipo de superclase, se utiliza la implementación original, la nueva implementación solo está disponible en la subclase.

Sin embargo, lo que a menudo se pasa por alto es que también es posible ocultar un método virtual.

class A
{
    public virtual void DoStuff() { // original implementation }
}

class B : A
{
    public new void DoStuff() {  //new implementation }
}

B b = new B();
A a = b;

b.DoStuff(); //calls new implementation
a.DoStuff(); //calls original implementation.

Tenga en cuenta que en el ejemplo anterior DoStuff se vuelve concreto y no se puede anular. Sin embargo, también es posible utilizar las palabras clave virtualy newjuntas.

class A
{
    public virtual void DoStuff() { // original implementation }
}

class B : A
{
    public new virtual void DoStuff() {  //new implementation }
}

class C : B
{
    public override void DoStuff() { //replacement implementation }
}

C c = new C();
B b = c;
A a = b;

c.DoStuff(); //calls replacement implementation
b.DoStuff(); //calls replacement implementation
a.DoStuff(); //calls original implementation.

Tenga en cuenta que a pesar de que todos los métodos involucrados son virtuales, la anulación en C no afecta el método virtual en A porque el uso de newen B oculta la implementación de A.

Editar: se señaló en los comentarios a esta respuesta que lo anterior puede ser peligroso o al menos no particularmente útil. Yo diría que sí, puede ser peligroso y estaría ahí fuera si fuera útil.

En particular, podría meterse en todo tipo de problemas si también cambia los modificadores de accesibilidad. Por ejemplo:-

public class Foo
{
    internal Foo() { }
    protected virtual string Thing() { return "foo"; }
}

public class Bar : Foo
{
 internal new string Thing() { return "bar"; }
}

Para un heredero externo de Bar, Foola implementación de Thing () sigue siendo accesible y anulable. Todo legal y explicable de acuerdo con las reglas de tipo .NET, nunca menos bastante poco intuitivo.

He publicado esta respuesta para profundizar la comprensión de cómo funcionan las cosas, no como una sugerencia de técnicas que se pueden usar libremente.

AnthonyWJones
fuente
2
Bueno saber eso. Aunque su uso puede ser peligroso :)
Stormenet
2
Todas las herramientas pueden ser peligrosas si no se utilizan correctamente.
AnthonyWJones
1
¡Pero su usabilidad realmente parece cuestionable! ¿Existe alguna aplicación de código abierto "seria" que utilice esto?
Jox
4
Usabilidad? ¿Te refieres a la utilidad? Algunas cosas que están al margen pueden parecer inútiles hasta que las necesite.
AnthonyWJones
Parece que el sombreado entra en escena SOLO cuando se juega virtual y nuevo. @ Stormenet está explicando solo cosas de anulación porque es un comportamiento normal de la clase llamar a su propio método a menos que sea virtual.
Sumit Kapadia
6

Creo que la principal diferencia es que con el sombreado, esencialmente estás reutilizando el nombre y simplemente ignorando el uso de superclase. Con la invalidación, está cambiando la implementación, pero no la accesibilidad y la firma (por ejemplo, tipos de parámetros y retorno). Consulte http://www.geekinterview.com/question_details/19331 .

Matthew Flaschen
fuente
5

El sombreado es un concepto de VB.NET. En C #, el sombreado se conoce como ocultación. Oculta el método de la clase derivada. Se logra usando la palabra clave 'nueva'.

La palabra clave Override se usa para proporcionar una implementación completamente nueva de un método de clase base (que está marcado como 'Virtual') en la clase derivada.

Shezi
fuente
Quiere decir. Oculta el método de la clase derivada en lugar de utilizar el método de la clase base. ?
shaijut
0

Básicamente, si tiene algo como a continuación,

Class A
{
}

Class B:A
{
}

A a = new B();

Cualquier método que llame al objeto 'a' se hará en el tipo de 'a' (aquí el tipo es 'A') Pero si implementa el mismo método en la clase B que ya está presente en la Clase A, el compilador darle una advertencia para que utilice una palabra clave "Nueva". Si usa "Nuevo", la advertencia desaparecerá. Aparte de esto, no hay diferencia entre usar "Nuevo" o no usarlo en la clase heredada.

En algunas situaciones, es posible que deba llamar a un método de la clase de referencia que contiene la instancia particular en ese momento en lugar de llamar a un método en el tipo de objeto. En el caso anterior, la referencia que contiene es 'B', pero el tipo es 'A'. Entonces, si desea que la llamada al método ocurra en 'B', entonces use Virtual y anule para lograr esto.

Espero que esto ayude...

Daniel Sandeep.

Daniel Sandeep
fuente
0

Si hay un caso en el que no puede alterar el contenido de una clase, digamos A, pero desea usar algunos de sus métodos y tiene un método cuyo nombre es común, puede usar su propia implementación de método mediante la newpalabra clave.

El punto crucial es usarlo que tanto la referencia como el objeto deben ser del mismo tipo.

class A
{
    public void Test()
    {
        Console.WriteLine("base");
    }
}

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

    public static void Main(string[] args)
    {
        A a = new A();
        B aa = new B();
        a.Test();
        aa.Test();
    }
}
snr
fuente