2010-10-01 11 views
44

Estoy un poco confundido acerca de 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 para cuando se usaría cada uno.Reemplazando el método ocultando

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 en el método en la clase secundaria, ¿se realizarán cambios en el método de 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? 
} 
+0

después de pasar por google he estado aquí con mis problemas ... – NoviceToDotNet

+0

Parece que necesita aprender conceptos básicos de programación de C#, antes de poder comprender el método de anulación y ocultación. Pruebe uno de los muchos libros de principiante C# alrededor. – thecoop

+0

1) a.m1(); // ¿Qué se imprimirá y por qué? Simplemente Imprimirá '" hai a todos "' porque está utilizando el concepto de anulación de Método. 2) b = a; // ¿Qué pasa aquí? Aquí obtendremos un error de compilación porque el objeto de clase hijo no se referirá al objeto de la clase Base. Y el reverso es posible de acuerdo con el concepto de Herencia. – Krish

Respuesta

101

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 

primordial es la manera clásica OO en el que una clase derivada puede tener un comportamiento más específico que una clase base (en algunos idiomas no tienes 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 BaseClass, se utiliza la funcionalidad definida en DerivedClass.

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

La mayoría de las veces, ocultarse 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. Compatibilidad hacia adelante. Si DerivedClass tenía un método DoStuff(), y luego BaseClass se cambió para agregar un método DoStuff(), (recuerde que pueden ser escritos por diferentes personas y existen en diferentes ensamblajes), entonces una prohibición de ocultación de miembros habría hecho DerivedClass con errores sin él cambiando. Además, si el nuevo DoStuff() en BaseClass era virtual, entonces hacerlo automáticamente en DerivedClass, una anulación del mismo podría hacer que se llame al método preexistente cuando no debería hacerlo. Por lo tanto, es bueno que el ocultamiento sea el predeterminado (utilizamos new para dejar en claro que definitivamente queremos ocultarlo, pero omitirlo oculta y emite una advertencia en la compilación).

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

Una cosa importante a observar con el segundo caso, es que hemos utilizado precisamente para ocultar quitamos sorpresas al código de llamada, ya que la persona que utiliza DerivedClass podría esperar razonablemente que su Clone() para devolver 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, a veces es necesario esconderse, con poca frecuencia, pero en general es malo, así que tenga mucho cuidado con él.

+0

señor ¿y si utilizo el vacío virtual público WriteNum() y en sub claass ..... uso público nuevo virual void WriteNum() qué método se invocará isReallyBase.WriteNum(); y y – NoviceToDotNet

+0

@NoviceToDotNet usando 'nuevo virtual' significa que oculta la versión más derivada, pero puede ser ella misma en sí misma.Si llama a 'WriteNum()' en 'isReallyBase' o' isReallyDerived', entonces se llamará a la versión definida en 'BaseClass', ya que no se sobrescribe, pero si la llamó en' isClearlyDerived', entonces la versión oculta ser llamado. Además, podría tener una anulación en otra subclase (digamos "DerivedFromDerivedClass") y esta anulación se invocaría cuando 'DerivedFromDerivedClass' se manipulara a través de una variable' DerivedFromDerivedClass' o una variable 'DerivedClass' ... –

+0

y también en caso si no invalidamos el método de super clase bcas, no es obligatorio, entonces lo que hará esReallyDerived.WriteStr(); – NoviceToDotNet

23

primordial es cuando se proporciona un nuevo override implementación de un método en una clase descendiente cuando se defina ese método en la clase base como virtual.

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

Ocultar 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 las siguientes 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 debe esperar; ambos métodos dicen que están ejecutando las versiones de InheritedClass.

Correr InheritedClass Método1
Correr InheritedClass Método2

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, en programación orientada a objetos principios, debe esperar el mismo resultado que en el ejemplo anterior. Pero no obtiene el mismo resultado:

Correr InheritedClass Método1
Correr BaseClass Método2

Cuando escribió el código InheritedClass, es posible que haya querido todas las llamadas a Method2() para ejecutar el código que escribió en ella. 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 oculto new /, llama a la versión de la referencia que está utilizando, en su lugar.


Si ese es el comportamiento que realmente quiere , 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.

+0

No diría que nunca debes _ ocultar_ un método. A veces, me resulta muy útil esconderme (por ejemplo, cambiando el valor de retorno de una subclase) – thecoop

+0

Es por eso que dije que debes -hacer- que nunca lo hagas :) –

+0

Sí, pero en caso de que @thecoop mencione que deseas deliberadamente trata de esconder Lo principal, creo, es que normalmente esconderse puede presentar sorpresas desagradables, pero al usarlo para la covarianza, en realidad se eliminan las sorpresas. –

2

Anulación de método es simple anulación de una implementación predeterminada de un método de clase base en la clase derivada.

de ocultación

Método: Se puede hacer uso de la 'nueva' palabra clave 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, tal puede anular foo1 que está definido en Bar.

+0

y puedo usar virtual con anulación de la palabra clave si en su ejemplo lo hago ... clase myclass: bar {entonces obtendrá la clase Foo foo1() o la clase bar foo1() para anularlo} también si está en la clase de la barra insinuado de ocultar el foo1() lo sobreescribo y pongo virtual ante él, entonces será heredado a myClass y de nuevo al reemplazarlo puedo anular el método de la clase Bar foo1() a myClass – NoviceToDotNet

+0

@NoviceToDotNet No, virtual puede ser usado en una clase derivada de Bar, pero anulará Bar.foo1 y no Foo.foo1. La combinación anterior de ocultar y virtual es válida, aunque al combinar los dos juntos quizás no sea la mejor explicación de la diferencia entre los dos. –

Cuestiones relacionadas