Polimorfismo en C#
Cómo utilizar polimorfismo, upcasting y downcasting para crear una funcionalidad potente y dinámica entre clases heredadas.
El polimorfismo es una característica de la herencia que permite que las clases tengan más de un tipo. Dentro de una jerarquía hereditaria cualquier clase derivada puede ser llamada una clase base. Esto significa que en cualquier momento que se necesite una clase base, una clase derivada puede usarse en su lugar.
Digamos que tenemos un juego que usa una jerarquía hereditaria, donde un
Orc (orco) y un
Goblin (duende) se derivan de
Enemy (enemigo). La misma que se deriva de
Humanoid (humanoide). Algo que tal vez querrás hacer es crear una colección de todos los objetos
Enemy en la escena. En vez de tener dos colecciones, una para todos los
Orc y una para todos los
Goblin, puedes tener una colección de objetos tipo
Enemy, de la cual tanto los objetos Orc como los objetos Goblin puedan ser parte.
class Enemy { /*Clase Base*/ }
class Orc : Enemy { /*...*/ }
class Goblin : Enemy { /*...*/ }
public class SomeClass : MonoBehaviour
{
// dos arrays de tipo hijos
public Orc[] Orcs;
public Goblin[] Goblins;
// un arrays de tipo padre
public Enemy[] Enemies;
void Start()
{
var myOrc = new Orc();
Orc.Add(myOrc);
var myGlobin = new Goblin();
Goblins.Add(myGlobin);
// El array Enemy admite ambos tipos por ser del tipo Base
Enemies.Add(myOrc);
Enemies.Add(myGlobin);
}
}De igual manera, si tienes una clase
Player que hereda de
Humanoid, luego podrás crear una colección de todos los objetos
Humanoid en una escena. Puede contener todos los objetos
Orc,
Goblin y
Player.
class Humanoid { /*Clase Base*/ }
class Player : Humanoid { /*...*/ }
class Enemy : Humanoid { /*...*/ }
class Orc : Enemy { /*...*/ }
class Goblin : Enemy { /*...*/ }
public class SomeClass : MonoBehaviour
{
public Humanoid[] Humanoids;
void Start()
{
var myPlayer = new Player();
var myEnemy = new Enemy();
var myOrc = new Orc();
var myGlobin = new Goblin();
// El array Enemy admite todos los tipos por ser del tipo Base
Humanoids.Add(myPlayer);
Humanoids.Add(myEnemy);
Humanoids.Add(myOrc);
Humanoids.Add(myGlobin);
}
}El polimorfismo también funciona para cosas como los parámetros de funciones. Toma en consideración las funciones
OnTrigger(). Generalmente tienen un parámetro
Collider el mismo que usualmente se lo llama algo como
other (otro) Los
GameObjects no tienen un componente
Collider dentro. No obstante, podrían tener un
Box Collider,
Sphere Collider,
Mesh Collider o algo similar.
public class SomeClass : MonoBehaviour
{
void OnbTriggerEnter(Collider other)
{
// do something fun!
Debug.Log(other.name);
}
}Cuando se invocan las funcionen
OnTrigger(), no sabemos qué tipo de
Collider se está usando. De hecho, el colisionador específico de cada objeto se pasa dentro de la función.
Desde que todos estos colisionadores diferentes heredan de la clase base
Collider, cualquiera de ellos funcionará.
Es importante saber que lo opuesto no es posible. Usando nuestro ejemplo previo, un orco es un enemigo, pero un enemigo no es un orco. No puedes tener algo que necesite una clase derivada y darle en vez una clase base.
Orc Is-A Enemy? : Yes
Enemy Is-A Orc? : No
Un uso muy genial del polimorfismo implica constructores y referencias a objetos.
Puedes declarar un objeto de un tipo de clase base y luego invocar el constructor de una de sus clases derivadas. Esto es porque la referencia de la variable espera algo del tipo de la clase base. Y el constructor de la clase derivada crea algo de un tipo derivado.
public class SomeClass : MonoBehaviour
{
void Start()
{
ParentClass myClass = new ChildClass();
}
}Si esto parece algo confuso, recuerda que la clase derivada es una clase base.
Por lo tanto, esta conversión funciona y es un proceso llamado conversión de
upcasted (ampliación) sólo puede ser tratado como un objeto de su clase base. En este ejemplo, mientras se amplía
ChildClass sólo puede tratarse como una
ParentClass o clase base. Esto significa que solo las variables y los métodos disponibles dentro de la clase base pueden usarse y serán tratados como si estuvieran en un objeto de la clase base. La excepción de esto son las funciones virtuales, las mismas que pueden invocar la versión con más
overrides.
Para obtener más información sobre las funciones
virtuales y cómo efectuar
overrides en ellas, consulta la sección de
overriding (sobrescritura)
.
public class SomeClass : MonoBehaviour
{
void Start()
{
ParentClass myClass = new ChildClass();
myClass.ParentMethod();
}
}Para tratar esta clase
ChildClass, como una clase derivada necesitamos restringir la variable
ChildClass de vuelta al tipo
ChildClass. Lo hacemos al rodear el nombre del tipo con paréntesis y poniéndolo en frente a la variable
downcasting. Podemos rodear esto con otro par de paréntesis y usar el operador de selección para acceder a los miembros o podemos crear una referencia a esta nueva versión.
public class SomeClass : MonoBehaviour
{
void Start()
{
// upcasted
ParentClass myClass = new ChildClass();
myClass.ParentMethod();
// downcasting
ChildClass myChild = (ChildClass) myClass;
myChild.ChildMethod();
}
}Apoya el contenido de mi blog a través de:
Fuente: