03/10/26 · 4 min de lectura

Polimorfismo en C#

Cómo utilizar polimorfismo, upcasting y downcasting para crear una funcionalidad potente y dinámica entre clases heredadas.

Imagen por Amanz .

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();
    }
}
Gist del proyecto

Apoya el contenido de mi blog a través de:

Fuente:

Copiado!