02/10/26 · 5 min de lectura

Generics en C#

Cómo crear y usar métodos y clases genéricos.

Imagen por Daniel Romero .

Los genéricos son una herramienta a través de la cual clases y métodos, entre otras cosas, pueden pasar un tipo como si fuera un parámetro. Esto, en efecto, te permite programar genéricamente sin saber el tipo exacto de datos con los que vas a trabajar.

Hemos visto antes en unity, que el método GetComponent() usa un parámetro genérico para obtener el tipo del componente que está buscando, GetComponent() es lo que se conoce como un método genérico, vamos a echarle un vistazo a cómo crear uno.

public class SomeClass
{
    public T GenericMethod<T>(T param)
    {
        return param;
    }
}

Aquí tenemos un ejemplo de un método genérico simple, lo primero a lo que debemos prestar atención es el parámetro genérico. Se llama “T” y está cercado por corchetes angulares. Después del nombre del método y antes de los parámetros normales o formales. Tal que esta T podría representar cualquier tipo, su nombre es arbitrario, pero la mayoría usan la letra “T” por costumbre. Igualmente, si te gustaría tener parámetros genéricos múltiples, puedes usar una coma y agregar más, la costumbre para nombrarlos usualmente sigue que los parámetros después de “T” son “U” y luego “V”.

public class SomeClass
{
    public T GenericMethod<T, U, V>(T paramT, U paramU, V paramV)
    {
        Console.WriteLine($"U: {paramU}, V: {paramV}");
        return paramT;
    }
}

Aunque las funciones genéricas no están limitadas solo a tres parámetros, es muy inusual ver una que tenga más de tres.

Sabemos ahora que el tipo genérico asociado con este método es “T”. La T es solo un marcador y terminará siendo en realidad un tipo cuando se invoque el método. Cualquier tipo que sea T también será el tipo de regreso y el tipo de argumento para el método desde que ambos usan T como su tipo.

Aunque tenemos un método que funciona con un tipo genérico, actualmente no es muy útil.

Entonces ¿qué podemos hacer con un tipo genérico?

Bien, ya que no sabemos cómo este tipo genérico actuará no podemos hacer mucho con él. Este parámetro genérico podría ser cualquier cosa, desde un float a un MonoBehaviour y más. Desde que no sabemos lo que es no hay muchas operaciones que podamos ejecutar con ello. No podemos, por ejemplo multiplicar nuestro MonoBehaviour por dos y no podemos acceder el campo GameObject de un float, por ahora se lo trata como si fuera el objeto de una clase, el cual es la clase base de la que todas las clases heredan en C# implícitamente.

Entonces, ¿Cómo ejecutamos más operaciones?

Para enterarnos sobre algunas de las características del tipo, tenemos que limitar qué tipo pueden ser poniendo limitaciones en el parámetro genérico, para agregar limitaciones a nuestra función, escribimos “where” después del parámetro, pero antes del cuerpo de la función. Esto sigue con el tipo genérico que vamos a limitar. En nuestro caso “T” y luego dos puntos, después de los dos puntos, tenemos las limitaciones específicas separadas por comas. Las limitaciones generalmente están dentro de cuatro categorías.

1. Puedes usar la palabra reservada class para asegurarte de que T sea un tipo de referencia.

public class SomeClass
{
    public T GenericMethod<T>(T param) where T : class
    {
        return param;
    }
}

O la palabra reservada struct para asegurarte de que es un tipo de valor.

public class SomeClass
{
    public T GenericMethod<T>(T param) where T : struct
    {
        return param;
    }
}

2. Puedes usar la palabra reservada new seguida de un par de paréntesis, para asegurarte de que tiene un constructor público sin parámetros

public class SomeClass
{
    public T GenericMethod<T>(T param) where T : new()
    {
        return param;
    }
}

3. Puedes usar el nombre de una clase para decir que T es esa clase o a través del polimorfismo, cualquier clase que se deriva de ella.

public class SomeClass
{
    public T GenericMethod<T>(T param) where T : MonoBehaviour
    {
        return param;
    }
}

4. La última categoría de limitaciones son las interfaces. Puedes usar el nombre de una interfaz para decir que T ha implementado esa interfaz.

public class SomeClass
{
    public T GenericMethod<T>(T param) where T : IEnumerable
    {
        return param;
    }
}

Para usar un método genérico debes especificar qué tipo querrías que use. Digamos que quieres usar el método genérico que acabas de crear. En otra clase escribirías el nombre del método seguido por corchetes angulares, luego el tipo que quieres. Lo seguirías entonces con los paréntesis y cualquier argumento.

public class SomeOtherClass : MonoBehaviour
{
    void Start()
    {
        SomeClass myClass = new SomeClass();
        myClass.GenericMethod<int>(5);
    }
}

Todas las características que hemos discutido se aplican a clases genéricas e interfaces y también a métodos. Al darle a una clase tipos genéricos puedes afectar los tipos de campo, propiedades y métodos en ella.

El crear clases genéricas es uno de los usos más comunes de los genéricos y hace que el implementar estructuras de datos sea muy fácil.

Ahora vamos a echarle un vistazo a la clase genérica.

Esta clase usa un tipo genérico de T, esto significa que cada instancia del tipo T que se usa como un tipo en la clase, será reemplazada con un tipo real cuando se esté usando. Esta clase tiene una variable miembro de un solo elemento llamada item que es de tipo T. La clase también tiene un método llamado UpdateItem() que toma un solo argumento tipo T y se lo asigna a el miembro item.

public class GenericClass <T>
{
    T item;

    public void UpdateItem(T newItem)
    {
        item = newItem
    }
}

Para instanciar un objeto de esta clase debes especificar un tipo para T.

Lo haces al escribir el nombre de la clase seguido de corchetes angulares y el tipo que quieras. También debes hacer esto después de escribir el nombre del constructor y antes de la lista de parámetros del constructor. Uno de los usos más comunes de los genéricos es para colecciones, como diccionarios y listas.

public class GenericClassExample : MonoBehaviour
{
    void Start()
    {
        GenericClass<int> myClass =  new GenericClass<int>();
        myClass.UpdateItem(5);
    }
}
Gist del proyecto

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

Fuente:

Copiado!