c# - remarks - Anulación vs método ocultando
returns c# (3)
Esta pregunta ya tiene una respuesta aquí:
- Sobrecarga y anulación 13 respuestas
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:
Adelante compatibilidad. Si
DerivedClass
tenía un métodoDoStuff()
, y luego se modificóBaseClass
para agregar un métodoDoStuff()
, (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. BuggyDerivedClass
sin que cambie. Además, si el nuevoDoStuff()
enBaseClass
era virtual, hacer automáticamente que enDerivedClass
una anulación del mismo podría llevar al método preexistente aDerivedClass
cuando no debería hacerlo. Por lo tanto, es bueno que el ocultamiento sea el predeterminado (usamos elnew
para dejar en claro que definitivamente queremos ocultarlo, pero omitirlo oculta y emite una advertencia en la compilación).La covarianza del pobre hombre Considere un método
Clone()
enBaseClass
que devuelve una nuevaBaseClass
que es una copia de la creada. En la anulación deDerivedClass
esto creará unaDerivedClass
pero la devolverá como unaBaseClass
, que no es tan útil. Lo que podríamos hacer es tener unaCreateClone()
protegida de forma virtual que esté anulada. EnBaseClass
tenemos unClone()
que devuelve el resultado de esto, y todo está bien, enDerivedClass
loDerivedClass
con un nuevoClone()
que devuelve unDerivedClass
. Llamar aClone()
enBaseClass
siempre devolverá una referencia deBaseClass
, que será un valor deBaseClass
o un valor deDerivedClass
según corresponda. Llamar aClone()
enDerivedClass
devolverá un valor deDerivedClass
, 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.