Estaba practicando la herencia, usando un programa de prueba en C# y descubrí que la siguiente declaración no arroja un error:
BaseClass baseObj = new DerivedClass();
¿Por qué se permite esta declaración y existe una situación en la que esta declaración sería útil para un programador?
Aquí está mi programa de prueba:
class BaseClass { public void show() { Console.WriteLine("Base Class!"); } } class DerivedClass : BaseClass { public void Display() { Console.WriteLine("Derived Class!"); } } class Result { public static void Main() { BaseClass baseObj = new DerivedClass(); baseObj.show(); } }
Te recomiendo que leas sobre herencia y polimorfismo con más detalle. ( aquí y aquí )
En esta respuesta trato de mantener los conceptos lo suficientemente simples.
¿Por qué se permite esta declaración y existe una situación en la que esta declaración sería útil para un programador?
Pero para explicar un poco su pregunta, echemos un vistazo al ejemplo simple y clásico de un programa orientado a objetos que necesita usar polimorfismo.
Suponga que está escribiendo un programa que necesita almacenar algunas formas y mostrarlas en la pantalla. Para lograr esto, debe almacenar todas las formas en una matriz, por ejemplo. ¿derecho?
Supongamos que nuestras clases son algo como esto:
class BaseShape { public virtual void Display() { Console.WriteLine("Displaying Base Class!"); } } class Circle : BaseShape { public override void Display() { Console.WriteLine("Displaying Circle Class!"); } } class Rectangle : BaseShape { public override void Display() { Console.WriteLine("Displaying Rectangle Class!"); } }
Y su matriz puede ser una matriz de object
. Me gusta esto:
object[] shapes = new object[10];
En su aplicación necesita escribir un método para mostrar formas.
Una solución puede ser iterar sobre todas las formas y llamar al método correcto del tipo exacto de forma. Me gusta esto:
public static void DisplayShapes_BAD(){ foreach(var item in Shapes) { if(typeof(Circle) == item.GetType()) { ((Circle)item).Display(); } if(typeof(Rectangle) == item.GetType()) { ((Rectangle)item).Display(); } } }
Pero, ¿qué sucede cuando aparece otro tipo de Shape
en la aplicación? Básicamente, debe modificar el método DisplayShapes_BAD()
para admitir un nuevo tipo de Shape
(agregue una nueva declaración if al cuerpo del método)
De esta manera, rompe el principio Abierto/Cerrado de la programación orientada a objetos. y su código no es muy fácil de mantener.
La mejor manera de almacenar formas para evitar este mal método es usar una matriz de BaseShape
. Me gusta esto:
public static List<BaseShape> Shapes = new List<BaseShape>();
A continuación se explica cómo agregar un elemento a esta lista de formas:
Shapes.Add(new Circle()); Shapes.Add(new Rectangle());
Ahora eche un vistazo a la buena implementación del método DisplayShapes.
public static void DisplayShapes_GOOD() { foreach(var item in Shapes) { item.Display(); } }
En el método anterior, llamamos al método de Display
en el elemento con el tipo de BaseShape
. Pero, ¿cómo sabe C # llamar al método correcto (por ejemplo, visualización circular o visualización rectangular)? Este mecanismo es polimorfismo.
Código completo compartido como Gist .
Según tengo entendido en Java, está intentando llamar al objeto de DerivedClass utilizando la referencia de BaseClass varibale baseobj y este escenario de codificación es totalmente válido porque proporciona la facilidad de polimorfismo en tiempo de ejecución.
Antes del polimorfismo en tiempo de ejecución, entendamos el Upcasting . Cuando la variable de referencia de la clase principal se usa para referirse al objeto de la clase secundaria, se denomina Upcasting
class A{} class B extends A{} A obj= new B // Upcasting.
El polimorfismo en tiempo de ejecución es un proceso en el que una llamada a un método anulado se resuelve en tiempo de ejecución en lugar de en tiempo de compilación.
Dado que no está anulando el método show en la clase derivada, no está haciendo polimorfismo en tiempo de ejecución, sino que simplemente es útil actualizar y actualizar cuando queremos resolver la llamada al método anulado en tiempo de ejecución.
En primer lugar, la pregunta de por qué está permitido es simplemente porque una instancia de la clase derivada es una instancia de la clase base (polimorfismo de subtipo). Lo mismo ocurre con la posibilidad de asignar cualquier clase derivada a una variable de objeto: todas las clases .net se derivan del objeto al final, por lo que también podría haber hecho object baseObj = new DerivedClass()
.
El objetivo del tipo que se utiliza para la declaración es indicar con qué tipo de interfaz se está trabajando (intento). Si declarara la variable como objeto, diría que solo la referencia es importante. Si declara como BaseClass, dice que está utilizando un objeto donde las propiedades y los métodos de BaseClass son importantes. Al usar BaseClass baseObj = new DerivedClass()
, está diciendo que necesita la funcionalidad de BaseClass, pero está usando una instancia de DerivedClass para definir el funcionamiento de la asignación descrita en BaseClass.
Una razón para esto podría ser que BaseClass es abstracto (BaseClasses a menudo lo son), desea un BaseClass y necesita un tipo derivado para iniciar una instancia y la elección de qué tipo derivado debe ser significativo para el tipo de implementación.
Una razón por la que se usa aún más a menudo es porque en cualquier momento se puede asignar otra clase derivada de BaseClass a la misma variable. Considerar:
BaseClass baseObj = SomeCriterium ? (BaseClass)new DerivedClass() : new AlternateDerivedClass();
El alcance de la variable en el ejemplo está solo en el método principal, pero si estuviera en cualquier parte de la clase, o pudiera cambiarse a través de una propiedad o de otra manera, al usar BaseClass, cualquier persona que use su clase podría asignar otra BaseClass (derivada) instancia, en lugar de solo instancias DerivedClass (derivadas).
Finalmente, un ejemplo para reasignar, usando una declaración de interfaz (en lo que respecta al polimorfismo, lo mismo se puede aplicar para declarar una interfaz implementada en lugar de la clase como se puede hacer con una clase base):
IEnumerable<T> values = new List<T>(); if(needfilter) values = values.Where(el => filtercriterium);
Si los valores se declararon como una lista, la variable de valores no se pudo reutilizar para la enumeración filtrada. Básicamente, primero dice que necesita la variable de valores solo para la enumeración. Después de eso, puede reasignar valores con otra enumeración en lugar de solo con una lista.