2007-05-21

Generics con tipos anonimos en C# 3.0

Hace unas semanas bajé el Beta 1 de Visual Studio Code Name Orcas, el cual trae soporte para el .NET Framework 3.5 y las herramientas de lenguaje para C# 3.0

Jugando un poco, primero con LINQ y después con los nuevos chiches de los tipos anónimos me encontré con el siguiente problema: pese a que haciendo queries de LINQ los valores de retorno podían venir como un tipo anónimo, yo no podía hacer lo mismo para mis clases.

Básicamente mi motivación era no tener que crear una clase para, por ejemplo, tener una colección de tuplas <Int, String> para poder hacer algo como esto:

(este código no compila. La notacion List<{Int, String}> es product de mi imaginación :D)

List<{Int, String}> miLista = new List<{Int, String}>();
miLista.Add(new { Elem1 = 1, Elem2 = "A" });
miLista.Add(new { Elem1 = 2, Elem2 = "B" });
miLista.Add(new { Elem1 = 3, Elem2 = "C" });

foreach (var tupla in miLista)
{
    Console.WriteLine(tupla.Elem1);
    Console.WriteLine(tupla.Elem2);
}



Le comenté esta duda a Diego Gonzalez y después de unos días me averiguó la forma de poder hacer esto y por que no funcionaba mi idea original. Me dijo que el problema es que los tipos genéricos no pueden ser inferidos en el constructor y es por eso que no se pueden crear las List<T> de esta forma.

Para poder crearlas hubo que hacer una clase auxiliar que infiere los tipos desde métodos estáticos y crea las instancias de las clases genéricas con los tipos ya inferidos (por si no se entendió, aca va un ejemplo):

(este código SI compila!)

using System;
using System.Collections.Generic;
using System.Text;

namespace CollectionsTest
{
    public class CollectionGenerator
    {
        public static List<T> NewList<T>(T witness)
        {
        return new List<T>();
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var miLista = CollectionGenerator.NewList(
            new { Elem1 = 1, Elem2 = "A" });

            miLista.Add(new { Elem1 = 1, Elem2 = "A" });
            miLista.Add(new { Elem1 = 2, Elem2 = "B" });
            miLista.Add(new { Elem1 = 3, Elem2 = "C" });

            foreach (var tupla in miLista)
            {
                Console.WriteLine(tupla.Elem1);
                Console.WriteLine(tupla.Elem2);
            }
        }
    }
}


Básicamente, lo que hace el método NewList de la clase CollectionGenerator es inferir el tipo de un testigo de una clase genérica y construir una lista del tipo inferido. Una vez hecho esto, ya podemos usar nuestra lista de objetos de un tipo anónimo!

Espero que les sea útil.

Un saludo!

Z

2 comentarios:

Anónimo dijo...

Z, la puta q te pario, pense que la sintaxis Listy<{String,String}> andaba...

Mati

Zaiden dijo...

El día que yo sea el encargado de hacer C#, va a andar :)