Reflectividad en tipos anónimos de C# 3

Muchos sabréis que C# 3 permite la creación de “Tipos anónimos”, es decir, la instanciación de objetos de clase desconocida. Para aquellos que aun no hayan hecho uso de esta característica, ahí va un ejemplo:

        private object ObtenerPersona()
        {
            return new { Nombre="Pablo", Apellido="Carballude" };
        }

Ese método devuelve un object, pero el objeto de retorno lo hemos creado sin especificar su tipo, simplemente hemos hecho el new y le hemos dado valor a unas propiedades que nos hemos inventado.

Dentro de ese método «ObtenerPersona», podríamos almacenar el nuevo objeto en una variable de tipo «var» y trabajar con sus propiedades. Sin embargo, ese método retorna un objeto anónimo, ¿Podemos obtener el valor de sus propiedades en el método que lo llama? Respuesta corta: No.

Evidentemente no hago un post para decir que no se puede :P La respuesta larga es: Sí, con reflectividad.

Si creamos un método que llame a «ObtenerPersona» y nos diga su tipo (mediante GetType()) descubriremos que la información que nos proporciona no nos sirve de nada :P ¿Solución? Darle un par de vueltas más.

Imaginemos que, mediante GetType obtenemos el tipo correcto del objeto anónimo. Teniendo su tipo, sería posible obtener una lista de todas sus propiedades (en este caso: Nombre y Apellido). Una vez que tengamos las propiedades, sería posible preguntarles por su valor. Dejemos de imaginar e implementemos:

private void MuestraDatos()
{
        //Obtengo el objeto anónimo
        var persona = ObtenerPersona();
        //Obtengo el tipo del objeto anónimo
        Type tipoPersona = persona.GetType();
        //Obtengo todas las propiedades del objeto anónimo
        PropertyInfo[] propiedades = tipoPersona.GetProperties();
        //Para cada propiedad...
        foreach (PropertyInfo propiedad in propiedades)
        {
	    //... creo un objeto con el valor de la propiedad...
	    object valorPropiedadActual = propiedad.GetValue(persona, null);
	    //... y muestro la información por pantalla
	    Console.WriteLine("Propiedad: {0} \nValor: {1}\n\n",
	        propiedad.Name, valorPropiedadActual.ToString());
        } 
}

Ese código produciría una salida:

Propiedad: Nombre
Valor: Pablo


Propiedad: Apellido
Valor: Carballude

Está claro que el uso de un tipo anónimo en este ejemplo para luego aplicar reflectividad está metido a calzador… pero creo que se entiende bastante bien como podría hacerse en caso de ser necesario.

En cualquier caso, la idea era ver como trabaja el compilador con estos «Tipos anónimos», ya que lo único que hace es crear una clase que se ajuste a los parámetros especificados en el momento de la creación.

Sería interesante ver si es posible la creación de tipos anónimos en tiempo de ejecución… habrá que probar otro día :)

Por Carballude

Me llamo Pablo Carballude González, soy graduado en computación con master en HCI y Seguridad Informática. Actualmente trabajo para Amazon en Seattle como Software Developer Engineer. Soy de esas personas que no saben si los textos autobiográficos deben ser en primera o tercera persona. Lo intenté en segunda, pero no le entendí nada :P

2 comentarios

  1. Hola, me pareció muy interesante el artículo, sin embargo tengo un caso en el que todavía no tengo claro como usar la reflexión:

    Quiero hacer un método que me permita asignar en forma dinámica un stored procedure con sus parametros y regresar una lista con los registros obtenidos. La idea es que este método sea de uso genérico, inicialmente intenté regresar un Datareader pero como la idea es cerrar la conexión tan pronto como se obtienen los resultados no me funciona. Un Dataset me parece algo pesado para una aplicación web y además solo se regresa una tabla como resultado. Entonces estaba pensando regresar una lista con los datos de tipo objeto, que serían agregados a partir de los resultados (obviamente hay que pasar el objeto base con el cual se debería llenar la lista).

    Mi código hasta el momento sería el siguiente:

    public List EstableceConsulta(string sStoredProcedure, Object claseBase, List Parametros = null)
    {

    using (DbConnection con = dpf.CreateConnection())
    {
    con.ConnectionString = CadenaConexion;
    using (DbCommand cmd = dpf.CreateCommand())
    {
    cmd.Connection = con;
    cmd.CommandText = sStoredProcedure;
    cmd.CommandType = CommandType.StoredProcedure;
    foreach ( clsParametro param in Parametros) {
    DbParameter mParam = dpf.CreateParameter();
    mParam.ParameterName = param.NomParam;
    mParam.Value = param.ValorParam;
    mParam.Direction = param.Direccion;
    if (param.Tamaño != 0) {
    mParam.Size = param.Tamaño;
    mParam.DbType = param.TipoDato;
    }
    cmd.Parameters.Add(mParam);
    }
    con.Open();
    using (DbDataReader dr = cmd.ExecuteReader(CommandBehavior.CloseConnection))
    {
    while (dr.Read())
    {
    aqui la idea es leer el esquema del Datareader e ir creando una lista de objetos del tipo ClaseBase, en donde cada
    elemento del DataReader se corresponde con una propiedad del objeto claseBase,
    por tanto, leyendo el esquema del DataReader, se podría tomar el nombre de cada columna y usarlo para «mapear»
    la propiedad correspondiente de la claseBase…y así crear un instancia de claseBase y agregarlo a la lista que
    finalmente se regresaría.

    }

    return ListaResultado;
    }
    }
    }
    return null;
    }

    Espero me puedas ayudar a resolver este tema, ya que además de este caso específico me ayudaría a implementar metodos genéricos que eviten codificar tanto.

    Saludos y Gracias!

  2. En el texto anterior quizá haya que aclarar que la ideas es tener algo mas o menos de esta forma

    dr = cmd.ExecuteReader();
    string columna = «»;
    DataTable dt;
    dt = dr.GetSchemaTable();
    while(dr.Read())
    {
    for (int i = 0; i < dt.tables[0].Columns.Count; i++)
    {
    Objeto.propiedad = dr[dt.Columns[i].ColumnName];
    }
    ListaResultado.add(Objeto);
    }

    return ListaResultado
    dr.Close();
    cn.Close();

    Obviamente aquí el tema es como asignar el valor a la propiedad correspondiente del objeto considerando que en cada llamada se pueden recibir objetos diferentes.

    Espero que sea clara mi pregunta.

    Saludos

Dejar un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *