Cómo: Generar tipos genéricos dinámicos

Hace un tiempo, empecé a preguntarme: “Cómo podría crear objetos genéricos específicos como List o Dictionary con tipos enviados como parámetros?”. Todos sabemos que entre “” tenemos que especificar el(los) tipo(s), pero ciertamente no podemos hacer algo como “Dictionary”.

Mi estimado colega Angel “Java” Lopez me dijo una vez que dicha creación dinámica de genéricos se podía hacer (no de esa forma, se refería a que el objetivo se podía alcanzar :P ). Así que comencé a investigar y preguntar, hasta que encontré el método Type.MakeGenericType(Type[] args). Leí la documentación y finalmente hice funcionar la idea! Así que lo compartiré con ustedes! :)

Código Ejemplo

Este ejemplo muestra una clase llamada FabricaDeGenericos que genera objetos genéricos de acuerdo con los tipos pasados como parámetros. Para este propósito, la clase expone un método llamado CrearInstanciaGenerica(Type tipoBase, Type[] tiposDeArgumento).

Parámetros:

  • tipoBase: el tipo que será el tipo base genérico, y
  • tiposDeArgumento: un arreglo (array) de argumentos que proveerá los tipos para los argumentos genéricos.

Devuelve:

  • object.

public class FabricaDeGenericos
{
    public object CrearInstanciaGenerica(
        Type tipoBase, Type[] tiposDeArgumento
        )
    {
        return Activator.CreateInstance(tipoBase.MakeGenericType(
            tiposDeArgumento));
    }
}

Esta clase recibe el tipo de la clase base del genérico y sus argumentos genéricos, y luego llama al método MakeGenericType(Type[] args) de la clase System.Type de tipoBase que construye el genérico.

Hora, podemos preparar nuestra clase Cliente (aquí es una Windows Console Application) para que use la clase FabricaDeGenericos de la siguiente manera:

class Program
{
    static void Main(string[] args)
    {
        FabricaDeGenericos miFabricaDeGenericos = new FabricaDeGenericos();

        Type[] misTiposDeArgumento = { typeof(string), typeof(object) };

        Dictionary<string, object> miDiccionario =
            (Dictionary<string, object>)miFabricaDeGenericos.CrearInstanciaGenerica(
                typeof(Dictionary), misTiposDeArgumento);

        Console.WriteLine(miDiccionario.GetType().FullName);
        Console.Read();
    }
}

Aquí, estoy creando un nuevo Dictionary. Si le dan una mirada al código del Cliente, le estoy enviando el objeto tipo base que representa el tipo genérico Dictionary omitiendo los argumentos tipo, pero manteniendo la coma que los separa, para que el compilador pueda inferir el número de parámetros tipo (y, por supuesto, para evitar excepciones :P ). Luego, también le envío un arreglo (array) de tipos (string para TKey y object para TValue) para reemplazar los parámetros tipo de Dictionary.

NOTA: La conversión (casting) es necesaria ya que el método CrearInstanciaGenerica() devuelve object! :)

Finalmente, llamo al método MakeGenericType(Type[] args) en el tipoBase el cuál devolverá el tipo para Dictionary. Activator.CreateInstance(Type TType) simplemente genera el objeto que será devuelto de acuerdo con el tipo.

La línea Console.WriteLine() muestra en la consola el tipo construido del nuevo objeto.

Más información:

- Salu2, Nacho

See this topic in English.

How To: Make dynamic generic types

Some time ago, I started wondering: “How could I create specific generic objects such as List or Dictionary with types sent as parameters?”. We all know that between “” we must provide the type(s), but we cannot certainly do something like “Dictionary”.

My dear workmate Angel “Java” Lopez told me once that such dynamic creation of generics could be done (not in that way, he meant that the objective could be reached :P ). So, I started searching and asking, till I found the method Type.MakeGenericType(Type[] args). I read the documentation and finally got the idea to work! So I’ll share this with you! :)

Sample Code

This example shows a GenericsFactory class that populates generic objects according to the types passed as parameters. For this purpose, the class exposes a CreateGenericInstance(Type baseType, Type[] typeArguments) method.

Parameters:

  • baseType: a type that will be the generic base type, and
  • typeArguments: an array of arguments which will provide the types for the generic arguments.

Returns:

  • object.

public class GenericsFactory
{
   public object CreateGenericInstance(
        Type baseType, Type[] typeArguments
        )
    {
        return Activator.CreateInstance(baseType.MakeGenericType(
            typeArguments));
    }
}

This class receives the generic base class type and its generics arguments, and then it calls the MakeGenericType(Type[] args) method of the System.Type class for the baseType which constructs the Generic.

Now, we can set our Client class (here’s a Windows Console Application) to use the GenericsFactory class as follows:

class Program
{
   static void Main(string[] args)
    {
        GenericsFactory myGenericsFactory = new GenericsFactory();

        Type[] myTypeArguments = { typeof(string), typeof(object) };

        Dictionary<string, object> myDictionary =
            (Dictionary<string, object>)myGenericsFactory.CreateGenericInstance(
            typeof(Dictionary), myTypeArguments);

        Console.WriteLine(myDictionary.GetType().FullName);
        Console.Read();
    }
}

Here, I’m creating a new Dictionary. If you have a look at the Client code, I’m sending the base type object representing the generic Dictionary type omitting the type arguments, but keeping the comma that separates them, so that the compiler can infer the number of type parameters (and, of course, to avoid exceptions :P ). Then, I also send an array of types (string for TKey and object for TValue) to replace the type parameters of Dictionary.

NOTE: Casting is needed as the CreateGenericInstance() method returns object! :)

Finally, I call the MakeGenericType(Type[] args) method on the baseType which will return the type for Dictionary. Activator.CreateInstance(Type TType) just populates the object that will be returned according to the type.

The Console.WriteLine() line displays in the console the built type of the new object.

More information:

- Cheers, Nacho

Ver este tópico en español.