returns remarks net name method example description c# override method-hiding

c# - remarks - Anulación vs método ocultando



returns c# (3)

Esta pregunta ya tiene una respuesta aquí:

Estoy un poco confundido sobre anular versus ocultar un método en C #. También se apreciarán los usos prácticos de cada uno, así como una explicación de cuándo se usaría cada uno.

Estoy confundido sobre la anulación, ¿por qué anulamos? Lo que he aprendido hasta ahora es que al eliminar podemos proporcionar la implementación deseada a un método de una clase derivada, sin cambiar la firma.

Si no anulo el método de la superclase y realizo cambios al método en la subclase, ¿se realizarán cambios en el método de la superclase?

También estoy confundido acerca de lo siguiente: ¿qué demuestra esto?

class A { virtual m1() { console.writeline("Bye to all"); } } class B : A { override m1() { console.writeLine("Hi to all"); } } class C { A a = new A(); B b = new B(); a = b; (what is this) a.m1(); // what this will print and why? b = a; // what happens here? }


Considerar:

public class BaseClass { public void WriteNum() { Console.WriteLine(12); } public virtual void WriteStr() { Console.WriteLine("abc"); } } public class DerivedClass : BaseClass { public new void WriteNum() { Console.WriteLine(42); } public override void WriteStr() { Console.WriteLine("xyz"); } } /* ... */ BaseClass isReallyBase = new BaseClass(); BaseClass isReallyDerived = new DerivedClass(); DerivedClass isClearlyDerived = new DerivedClass(); isReallyBase.WriteNum(); // writes 12 isReallyBase.WriteStr(); // writes abc isReallyDerived.WriteNum(); // writes 12 isReallyDerived.WriteStr(); // writes xyz isClearlyDerived.WriteNum(); // writes 42 isClearlyDerived.writeStr(); // writes xyz

Anulación es la forma clásica de OO en la que una clase derivada puede tener un comportamiento más específico que una clase base (en algunos idiomas no tiene más remedio que hacerlo). Cuando se llama a un método virtual sobre un objeto, se llama a la versión más derivada del método. Por lo tanto, aunque se trata de isReallyDerived como una BaseClass se BaseClass funcionalidad definida en DerivedClass .

Ocultar significa que tenemos un método completamente diferente. Cuando llamamos a WriteNum() en isReallyDerived , no hay forma de saber que hay un WriteNum() diferente en DerivedClass por lo que no se llama. Solo se puede DerivedClass cuando se trata del objeto como una DerivedClass .

La mayoría de las veces, esconderse es malo. En general, o bien debe tener un método como virtual si es probable que se modifique en una clase derivada, y anularlo en la clase derivada. Sin embargo, hay dos cosas para las cuales es útil:

  1. Adelante compatibilidad. Si DerivedClass tenía un método DoStuff() , y luego se modificó BaseClass para agregar un método DoStuff() , (recuerde que pueden ser escritos por diferentes personas y existen en diferentes ensamblajes), la prohibición de la ocultación de miembros se habría producido repentinamente. Buggy DerivedClass sin que cambie. Además, si el nuevo DoStuff() en BaseClass era virtual, hacer automáticamente que en DerivedClass una anulación del mismo podría llevar al método preexistente a DerivedClass cuando no debería hacerlo. Por lo tanto, es bueno que el ocultamiento sea el predeterminado (usamos el new para dejar en claro que definitivamente queremos ocultarlo, pero omitirlo oculta y emite una advertencia en la compilación).

  2. La covarianza del pobre hombre Considere un método Clone() en BaseClass que devuelve una nueva BaseClass que es una copia de la creada. En la anulación de DerivedClass esto creará una DerivedClass pero la devolverá como una BaseClass , que no es tan útil. Lo que podríamos hacer es tener una CreateClone() protegida de forma virtual que esté anulada. En BaseClass tenemos un Clone() que devuelve el resultado de esto, y todo está bien, en DerivedClass lo DerivedClass con un nuevo Clone() que devuelve un DerivedClass . Llamar a Clone() en BaseClass siempre devolverá una referencia de BaseClass , que será un valor de BaseClass o un valor de DerivedClass según corresponda. Llamar a Clone() en DerivedClass devolverá un valor de DerivedClass , que es lo que queremos en ese contexto. Existen otras variantes de este principio, sin embargo, debe tenerse en cuenta que son muy raras.

Una cosa importante a tener en cuenta en el segundo caso es que hemos utilizado el ocultamiento con precisión para eliminar sorpresas del código de llamada, ya que la persona que utiliza DerivedClass podría esperar razonablemente que su Clone() devuelva un DerivedClass . Los resultados de cualquiera de las formas en que podría llamarse se mantienen consistentes entre sí. La mayoría de los casos de ocultamiento se arriesgan a introducir sorpresas, por lo que en general son mal vistos. Éste se justifica precisamente porque resuelve el problema que el ocultamiento a menudo introduce.

En general, ocultar es a veces necesario, infrecuentemente útil, pero generalmente malo, así que ten cuidado con él.


La anulación es cuando se proporciona una nueva implementación de override de un método en una clase descendiente cuando ese método se define en la clase base como virtual .

Ocultar es cuando proporciona una nueva implementación de un método en una clase descendiente cuando ese método no está definido en la clase base como virtual , o cuando su nueva implementación no especifica la override .

Esconder es a menudo malo; en general, debes tratar de no hacerlo si puedes evitarlo en absoluto. Ocultar puede hacer que ocurran cosas inesperadas, porque los métodos ocultos solo se usan cuando se invoca una variable del tipo real que se definió, no si se usa una referencia de clase base ... por otro lado, los métodos virtuales que se anulan terminarán con se llama a la versión de método apropiada, incluso cuando se llama utilizando la referencia de clase base en una clase secundaria.

Por ejemplo, considere estas clases:

public class BaseClass { public virtual void Method1() //Virtual method { Console.WriteLine("Running BaseClass Method1"); } public void Method2() //Not a virtual method { Console.WriteLine("Running BaseClass Method2"); } } public class InheritedClass : BaseClass { public override void Method1() //Overriding the base virtual method. { Console.WriteLine("Running InheritedClass Method1"); } public new void Method2() //Can''t override the base method; must ''new'' it. { Console.WriteLine("Running InheritedClass Method2"); } }

Vamos a llamarlo así, con una instancia de InheritedClass, en una referencia coincidente:

InheritedClass inherited = new InheritedClass(); inherited.Method1(); inherited.Method2();

Esto devuelve lo que deberías esperar; ambos métodos dicen que están ejecutando las versiones de InheritedClass.

Ejecutando el método InheritedClass1
Ejecución del método InheritedClass2

Este código crea una instancia de la misma, InheritedClass, pero la almacena en una referencia BaseClass:

BaseClass baseRef = new InheritedClass(); baseRef.Method1(); baseRef.Method2();

Normalmente, según los principios de OOP, debe esperar el mismo resultado que en el ejemplo anterior. Pero no obtienes el mismo resultado:

Ejecutando el método InheritedClass1
Ejecución del método BaseClass2

Cuando escribió el código de InheritedClass, es posible que haya deseado todas las llamadas a Method2() para ejecutar el código que escribió en él. Normalmente, así sería como funciona, suponiendo que está trabajando con un método virtual que ha anulado. Pero debido a que está utilizando un método new / oculto, en su lugar llama a la versión de la referencia que está utilizando.

Si ese es el comportamiento que realmente quieres , entonces; ahí tienes. Pero le sugiero encarecidamente que, si eso es lo que quiere, puede haber un problema arquitectónico más grande con el código.


La sobrescritura del método reemplaza de manera simple una implementación predeterminada de un método de clase base en la clase derivada.

Método de ocultación: puede utilizar la palabra clave ''nueva'' antes de un método virtual en una clase derivada

como

class Foo { public virtual void foo1() { } } class Bar:Foo { public new virtual void foo1() { } }

ahora si haces otra clase Bar1 que se deriva de Bar, puedes anular foo1 que está definido en Bar.