Generics en C#

1 - Qué son Generics en C#

Generics es una funcionalidad que nos da .NET la cual nos permite crear código reusable entre múltiples entidades. 

Cuando creamos código genérico lo hacemos para que sea compatible con cualquier tipo de dato y por ello, “seguro” ante diferentes tipos. 

 

2 - Ejemplo de Generics en C#

La forma más sencilla de demostrar generics es utilizando código ejemplo.

Para ello utilizare código que presente en una prueba técnica, la cual cumple perfectamente con lo acometido.

En dicha entrevista tenía que realizar diferentes llamadas API y devolver el resultado si estaba bien etc, por ejemplo, habia una opcion que era get/{id} el cual devolvia un objeto Persona.

Para ello utilicé una clase llamada OperationResult la cual contiene 3 propiedades.

  • Bool Success 
  • List<string> mensageList
  • Persona Persona

Englobar los objetos dentro de otro nos puede ayudar en caso de que tengamos una aplicación leyendo de nuestra API. Ya que de ese modo facilitaremos la vida al cliente.

public class OperationResult 
{
    public bool Success => !MessageList.Any();
    public List<string> MessageList { get; private set; }
    public Persona Persona { get; set; }

    public OperationResult()
    {
        MessageList = new List<string>();
    }

    public void AddMessage(string message)
    {
        MessageList.Add(message);
    }

    public void SetSuccesResponse(Persona pers)
    {
        Persona = pers;
    }   
}

 A su vez, tenemos otro endpoint que debemos devolver el objeto Coche.

public class OperationResultCars
{
    public bool Success => !MessageList.Any();
    public List<string> MessageList { get; private set; }
    public Car Coche { get; set; }

    public OperationResult()
    {
        MessageList = new List<string>();
    }

    public void AddMessage(string message)
    {
        MessageList.Add(message);
    }

    public void SetSuccesResponse(Car coche)
    {
        Coche = coche;
    }
}

Como podemos observar ambos objetos son prácticamente iguales, por lo que estamos repitiendo mucho código. en nuestro ejemplo en concreto estamos repitiendo 21 de 23 líneas de código por lo que no estamos cumpliendo la norma de “no repetir código”.

 

3 - Convertir nuestro Código a generic en C#

Para convertir nuestro código a generic debemos reemplazar el código que es diferente por su parte “genérica”. por lo que si vemos los dos ejemplos anteriore, únicamente debemos “juntar” los tipos Persona y Car

Pero estos tipos deben de seguir existiendo en nuestro objeto, ya que los vamos a necesitar.

Para ello convertimos la clase anterior de la siguiente forma:

public class OperationResult <T>
{
    public bool Success => !MessageList.Any();
    public List<string> MessageList { get; private set; }
    public T Response { get; set; }

    public OperationResult()
    {
        MessageList = new List<string>();
    }

    public void AddMessage(string message)
    {
        MessageList.Add(message);
    }

    public void SetSuccesResponse(T obj)
    {
        Response = obj;
    }   
}

Como podemos observar hay una sola diferencia, la principal es que hemos convertido los tipos Persona y Car en el tipo T justo después del nombre de la clase, con lo que nuestra clase ya es genérica.

generics en c#

Por convención, cualquier programador que trabaje con generics, utilizar la letra T para indicar que es el tipo.

Finalmente, indicar que el tipo T aceptará cualquier tipo, tanto uno creado por nosotros como puede ser la clase o tipo Persona o un tipo como puede ser el tipo string

 

3.1 - Múltiples tipos en una clase genérica en c#

Por supuesto podemos implementar más de un tipo en nuestra generic class, para ello debemos pasar más de un tipo junto al nombre de la clase

public class OperationResult <T, U>
{
    //Code
}

De todas formas, por convención, cuando introducimos más de un tipo, debemos utilizar más de una letra, para hacer la vida más sencilla a los programadores que vengan detrás de nosotros. 

public class OperationResult <TEntrada, TSalida>
{
    //Code
}

 

4 - Uso de una clase genérica

cuando implementamos una clase genérica implementamos un tipo, eso quiere decir que cuando instanciamos la clase genérica debemos indicar el tipo que vamos a utilizar, para ello instanciamos la variable de la siguiente manera:

OperationResult<Car> optResult = new OperationResult<Car>();

Recordar que T dentro de nuestra clase, acepta cualquier tipo. por lo que nos servirá tanto para la clase Car, como Persona, etc. 

 

5 - Métodos Genéricos en c#

Las clases no son las únicas partes que pueden ser genéricas en nuestro código, también podemos hacer métodos genéricos. Para ello tenemos dos opciones

A - Método genérico en clase genérica 

Primero podemos tener un método que recibe un parámetro T o devuelve un parámetro T lo cual es completamente válido, como hemos visto en el ejemplo anterior:

public void SetSuccesResponse(T obj)
{
    Response = obj;
}

B - Método genérico en clase NO genérica 

Si disponemos de una clase no genérica, podemos crear un método que acepte tipos genéricos, quizá no son tan comunes, pero se suelen ver de vez en cuando, sobre todo en clases base. 

Para tener un método genérico en una clase no genérica lo hacemos de la siguiente manera:

public class Ejemplo{
    public T GenericMethod<T>(T receivedGeneric)
    {
        return receivedGeneric;
    }  
}

Indicamos después del nombre del método el tipo <T>

Y como vemos podemos devolver también el propio tipo.

 

6 - Consejos de Generics en C#

  • Debemos utilizar Generics para crear código reusable y clases o métodos que no están enlazadas a tipos. Pero debemos ser cuidadosos, ya que si una clase no va a ser reutilizada, NO debemos hacerla genérica, ya que añade una gran complejidad a nuestro código. 
  • Debemos utilizar la letra T cuando solo tenemos un tipo parámetro, pero si tenemos más de uno, debe ser un nombre más descriptivo.
  • Pese a ello, debemos poner los nombres de los parámetros empezando por T, para indicar a quien lea nuestro código que es un Tipo

 

7 - Condiciones en los tipos

Cuando declaramos una clase o un método genérico, tenemos la capacidad de extender la condición de que tipo de objeto vamos a pasar como tipo.

para ello utilizaremos la cláusula where T : {condición}

public interface IExample {}
public class Example : IExample{}

public class EjemploGeneric <T>
where T : struct //tipo valor 
where T : class // tipo referencia
Where T : new() //constructor sin parametros
Where T : IExample //Interfaz concreta
Where T : Example //una clase concreta
{

}

Podemos introducir condiciones para todos los tipos que utilizamos, además podemos incluir varias condiciones para un solo tipo, por ejemplo IExample, new() te obligará a indicar una clase, que implemente IExample además de tener un constructor sin parámetros. 

Conclusión

  • Utilizaremos Generics como forma o técnica de definir múltiples tipos de datos con una sola variable. 
  • Lo cual nos permite crear código reusable de una forma segura, que además funciona con cualquier tipo de dato.
  • Altamente recomendable utilizar generics tanto como podamos, pero con cabeza porque a la vez que mejoramos la calidad de nuestro código podemos añadirle cierta dificultad.