Downcasting de un list<clase>

855 views
Skip to first unread message

Nestor Rodriguez

unread,
Feb 23, 2013, 8:53:37 AM2/23/13
to altnet-...@googlegroups.com

Que tal amigos de alt net

 

Estoy queriendo hacer un downcastin de un List  pero me da error.

Por ejemplo:

Tengo:

  interface IFilaListado

    {

    }

 

  class ResumenCobrador:IFilaListado

    {

       . . .

    }

 

Un método me devuelve un List<IFilaListado>:

private List<IFilaListado> _resumen() {. . .}

pero el listado es en realidad es un List< ResumenCobrador>.

 

La pregunta es, ¿cómo puedo hacer algo como esto?

List<ResumenCobrador> rc =  (List<ResumenCobrador>)variable (que es un List<IFilaListado>)

 

Desde ya gracias por su tiempo.

 

Atte.

Néstor

Ale Miralles

unread,
Feb 23, 2013, 9:29:49 AM2/23/13
to altnet-...@googlegroups.com

La respuesta corta es que no se puede (no es safely covariant).

Un workaround es castear cada uno de los ítems de la lista, se puede llegar a hacer con linq para que quede un poco mas elegante.

Ahora, si estas en .NET 4+ tenes la opción de utilizar IEnumerable<out T> y en ese caso si vas a poder castear la colección directamente.

Aca tenes un ejemplo:

http://stackoverflow.com/questions/12424537/why-ienumerablet-is-defined-as-ienumerableout-t-not-ienumerablet

 

Saludos, Ale Miralles

http://amiralles.com.ar

http://blog.amiralles.com.ar

--
Has recibido este mensaje porque estás suscrito al grupo "AltNet-Hispano" de Grupos de Google.
Para anular la suscripción a este grupo y dejar de recibir sus correos electrónicos, envía un correo electrónico a altnet-hispan...@googlegroups.com.
Para publicar una entrada en este grupo, envía un correo electrónico a altnet-...@googlegroups.com.
Visita este grupo en http://groups.google.com/group/altnet-hispano?hl=es.
Para obtener más opciones, visita https://groups.google.com/groups/opt_out.
 
 

Lester Quintero

unread,
Feb 23, 2013, 9:42:09 AM2/23/13
to altnet-...@googlegroups.com
Creo que esto te puede ayudar:

var result1 = s.Select(c => (ResumenCobrador)c); 



--

nesto...@gmail.com

unread,
Feb 23, 2013, 9:54:54 AM2/23/13
to altnet-...@googlegroups.com

Gracias por responder, prueba como me diste el ejemplo y me rechaza respondiendo:

 

Error      1             No se puede convertir implícitamente el tipo 'System.Collections.Generic.IEnumerable<CobranzasLibrary.ClasesDatos.ResumenCobrador>' en 'System.Collections.Generic.List<CobranzasLibrary.ClasesDatos.ResumenCobrador>'. Ya existe una conversión explícita (compruebe si le falta una conversión)               C:\Users\ingres\Documents\Visual Studio 2012\Projects\Cobranzas\CobranzasLibrary\Cobranza.cs           16           20           CobranzasLibrary

 

Este es el método:

    public List<ResumenCobrador> listadoResumenCobrador() {

            Listados lis = new Listados(new ResumenCobrador());     

            return lis.getFilas().Select(c=>(ResumenCobrador)c);      ç aquí lis.getFilas() es un List< IFilaListado>       

            

        }

 

Desde ya gracias por responder.

 

Atte.

Néstor

Angel Java Lopez

unread,
Feb 23, 2013, 10:00:02 AM2/23/13
to altnet-...@googlegroups.com
Ah! el return probar
return lis.getFilas().Select(c=>(ResumenCobrador)c).ToList()

Es raro que yo use un metodo que devuelva List, prueba tmb poner que
devuelva IList

Pero si lo unico que necesitas es recorrer el resultado, pondria que
el metodo devueval IEnumerable<...> y el return lo dejaria como esta
en tu codigo

2013/2/23 <nesto...@gmail.com>:

Alpha

unread,
Feb 23, 2013, 10:24:26 AM2/23/13
to altnet-...@googlegroups.com
El método simple para eso es Cast<T>();

getFilas().Cast<ResumenCobrador>()



2013/2/23 Angel Java Lopez <ajlop...@gmail.com>

nesto...@gmail.com

unread,
Feb 23, 2013, 11:01:56 AM2/23/13
to altnet-...@googlegroups.com
Funciono!! Con el .ToList.
Lo que pasa es que mi idea es tener una clase que se encarga de pasarme solo
listados de diferentes "TIPOS" llamando siempre a un solo método que sabe
que responderme porque le doy como parámetro el objeto de la clase que me
sirve de FILA para el listado y como todos implementan IFilaListado el
método me lo acepta, esto creo que se llama POLIMORFISMO.

Aquí es donde estaba mi problema, el método UNICO me devolvía el listado, me
devolvía un listado con objetos del tipo IFilaListado, por lo que debería
hacer un DownCasting para poder usar el objeto.
Pero gracias a ti logre solucionar mi problema, es como un vaso de agua fría
en pleno verano, se siente refrescante. Muchas gracias.

Atte.
Néstor

-----Mensaje original-----
De: altnet-...@googlegroups.com [mailto:altnet-...@googlegroups.com]
En nombre de Angel Java Lopez
Enviado el: sábado, 23 de febrero de 2013 12:00
Para: altnet-...@googlegroups.com
Asunto: Re: [altnet-hispano] Downcasting de un list<clase>

nesto...@gmail.com

unread,
Feb 23, 2013, 11:04:07 AM2/23/13
to altnet-...@googlegroups.com

Gracias!!, tu sugerencia también me funciono pero agregándole .ToList

lis.getFilas().Cast<ResumenCobrador>().ToList();

 

Atte.

Nestor

Fernando Pelliccioni

unread,
Feb 25, 2013, 11:55:24 AM2/25/13
to altnet-...@googlegroups.com
Nestor,

El downcasting tiene un costo de procesamiento en runtime.
Casi siempre que haces downcasting, se pude decir que tenés un problema de diseño.
¿Por qué tenés la necesidad de hacer downcasting la lista retornada?

Te recomiendo pegarle una mirada al patrón Visitor, o mejor, usar alguna librería que te permita hacer Type Switch eficientemente sobre jerarquías de clases abiertas ( ó cerradas, según sea tu caso ). Aunque no estoy seguro que exista una librería así para C#.

Saludos,
Fernando Pelliccioni.


2013/2/23 <nesto...@gmail.com>

Miguel Madero

unread,
Feb 25, 2013, 12:44:55 PM2/25/13
to altnet-...@googlegroups.com
Nestor, 

Concuerdo con lo que dice Fernando y esto es facil de corregir, solo tienes que cambiar la firma del metodo resumen para hacerlo generico.

private List<T> _resumen<T>() where T : IFilaListado {. . .}

De esta forma, depende de que le mandes es lo que te regresa. 

Por cierto, sugiero tambien cambiar el nombre para usasr las convenciones de C# (Resumen seria un nombre mas apropiado).



Miguel


2013/2/25 Fernando Pelliccioni <fpelli...@gmail.com>

nesto...@gmail.com

unread,
Feb 25, 2013, 12:59:24 PM2/25/13
to altnet-...@googlegroups.com

Y que pasa con el polimorfismo?, lo que pasa es que necesito que un  objeto me devuelva una colección de diferentes objetos llamando a un método, que tiene como parámetro el Objeto que tiene que cargarme de datos y devolvérmelo dentro de una colección List, y me salió y funciona.

Y ahora me entero de que es un mal diseño, ufff, creo que tengo que  estudiar más OOP. Jeje.

 

Pero muchas gracias por las respuestas y sugerencias.

Carlos Peix

unread,
Feb 25, 2013, 1:14:08 PM2/25/13
to altnet-...@googlegroups.com
Hola Nestor,

El polimorfisto sigue intacto. Lo que estas experiementando es el problema que impone la covarianza y contravarianza en los lenguajes de tipado estatico que implementan tipos parametrizados (generics).

Sugiero que leas sobre covarianza y contravarianza.

Lo que te sugieren en los ultimos mails es algo que no tiene nada que ver con lo que te menciono sino con tratar de utlzar el tipo de retorno mas generico posible (IList<> o IEnumerable<>)

----------------------------------
Carlos Peix

2013/2/25 <nesto...@gmail.com>

Fernando Pelliccioni

unread,
Feb 25, 2013, 1:38:45 PM2/25/13
to altnet-...@googlegroups.com
Polimorfismo.... bueno...

Hay varios tipos de polimorfismo.

En polimorfismo "clásico" de los lenguajes como C# (se llama Dynamic, Subtype ó Inclusion Polymorphism) ( Lo llamo clásico porque es al que la mayoría de los programadores se refiere con la sola palabra "polimorfismo" ), justamente me parece que lo estás aplicando mal.
Si creaste una *interface* es porque me imagino que determinaste que hay un comportamiento en común entre todas las clases que pensás retornar.
El parámetro que le pasas a Resumen() para determinar el tipo ¿es conocido en Compile-Time o recién en Runtime?
Si recién lo vas a conocer en Runtime (creo que es tu caso), podés usar polimorfismo dinámico sin necesidad de hacer downcasting.

Imagino que tenés un código similar al siguiente ...

        interface IFilaListado
        {
            void DoWork();
        }

        class ResumenCobrador : IFilaListado
        {
            public void DoWork()
            {
                Console.WriteLine("ResumenCobrador");
            }
        }

        class OtraClase : IFilaListado
        {
            public void DoWork()
            {
                Console.WriteLine("OtraClase");
            }
        }

        List<IFilaListado> Resumen()
        {
            var result = new List<IFilaListado>();
            result.Add( /* ... */ );
            return result;
        }

        void Foo()
        {
            List<IFilaListado> list = Resumen();

            foreach (var item in list)
            {
                item.DoWork(); 
            }
        }

¿ Qué uso le vas a dar a la lista retornada por Resume() ?

Saludos,
Fernando Pelliccioni.

PD: Consulta, es la primer vez que escribo en esta lista, ¿cúal es el posting-style que suelen usar? Gracias!


2013/2/25 <nesto...@gmail.com>

nelopa...@gmail.com

unread,
Feb 25, 2013, 2:42:16 PM2/25/13
to altnet-...@googlegroups.com
Nestor, creo que el tema del diseño viene por el lado de que si tenés
una porción de código que trabaja con IFilaListado, esa porción de
código no debería necesitar conocer mas que IFilaListado, si estás
necesitando conocer la subclase hay algo que huele mal.

En otras palabras, el código que funciona con IFilaListado debería
hacerlo con cualquier implementación (conocida o desconocida) de
IFilaListado.

saludos.
nelo

2013/2/25 Fernando Pelliccioni <fpelli...@gmail.com>:

Vicenç Garcia

unread,
Feb 25, 2013, 2:43:10 PM2/25/13
to altnet-...@googlegroups.com

nelopa...@gmail.com

unread,
Feb 25, 2013, 2:47:26 PM2/25/13
to altnet-...@googlegroups.com
exacto

2013/2/25 Vicenç Garcia <vincen...@gmail.com>:

nesto...@gmail.com

unread,
Feb 25, 2013, 3:13:28 PM2/25/13
to altnet-...@googlegroups.com

Disculpa mi ignorancia pero coloco de esa manera y me genera el siguiente error.

 

Error      4             No se puede convertir implícitamente el tipo 'System.Collections.Generic.List<CobranzasLibrary.ClasesDatos.ResumenCobrador>' en 'System.Collections.Generic.List<T>'    C:\Users\ingres\Documents\Visual Studio 2012\Projects\Cobranzas\CobranzasLibrary\Hacer\Listados.cs               62           20           CobranzasLibrary

 

El método completo es:

     private List<T> _resumen<T>() where T :IFilaListado {

            string sql = @"select cod_cobrador, cobrador, sum(cant_clientes) cant_clientes,

                           sum(cant_facturas) cant_facturas,

                           int4(sum(monto)) importe, max(momento_carga) momento_carga,

                           max(periodo) periodo

                from rendiciones_info_resumen r

                where clase = 'R'

                and periodo =  (select periodo

                                                from periodos_cobranza p

                                                where p.fec_desde <= date('today')

                                                and   p.fec_hasta >= date('today'))

                group by cod_cobrador, cobrador

                order by cobrador";

 

            DataTable Tabla = _conneccionIngres.runSelect(sql);

            List<ResumenCobrador> listado = new List<ResumenCobrador>();

            foreach (DataRow row in Tabla.Rows) {

                ResumenCobrador rc = new ResumenCobrador();

                rc.codCobrador = row["cod_cobrador"].ToString();

                rc.cobrador = row["cobrador"].ToString();

                rc.cantClientes = int.Parse(row["cant_clientes"].ToString());

                rc.cantFacturas = int.Parse(row["cant_facturas"].ToString());

                rc.importe = int.Parse(row["importe"].ToString());

                rc.momentoCarga = (DateTime)row["momento_carga"];

                rc.periodo = (DateTime)row["periodo"];               

                listado.Add(rc);

            }

            return listado;

        }

 

Esto de <T> es algo nuevo para mi, si pudieras darme una pequeña explicación o algún enlace para leerlo.

 

Desde ya gracias.

 

Atte.

Nestor

Miguel Madero

unread,
Feb 25, 2013, 4:46:36 PM2/25/13
to altnet-...@googlegroups.com
Estas creando una lista de ResumenCobrador
 List<ResumenCobrador> listado = new List<ResumenCobrador>();

Parece que siempre regresas este tipo, de ser ese el caso, cambia la firma para regresar List<ResumenCobrador> si quieres regresar diferents tipos de IFilalListado entonces cambia esa linea para crear una Lista de tipo de T

 List<T> listado = new List<T>();


Te sugiero leer sobre el tema de generics. 



Miguel


2013/2/25 <nesto...@gmail.com>

nesto...@gmail.com

unread,
Feb 26, 2013, 9:09:47 AM2/26/13
to altnet-...@googlegroups.com

Gracias por tu sugerencia Miguel Madero, ahora ya me queda claro lo de generics.

 

Sin embargo aún necesito tener un solo método que me devuelva colecciones List con diferentes tipos dentro de la colección.

 

Me explico

Tengo una clase listado y un método publico getFilas, que son los que me van a proveer de diferentes tipos de listados.

Para que se me entienda mejor copio la clase:

public class Listados

    {

        private IFilaListado _fila;

 

        public Listados(IFilaListado fila) { //Gracias a fila el método getFilas sabe que Listado devolverme

            this._fila = fila;

        }

 

        public List<IFilaListado> getFilas() {

            switch (this._fila.GetType().ToString()) {//Aquí decide qué TIPO de listado Retornar

                case "ResumenCobrador":

                    return this._resumen();

 

               [. . .] //Diferentes casos

                //break;

                default:

                    return new List<IFilaListado>();

                //break;

            }

        }

 

        //Metodos

        private List< IFilaListado > _resumen() {//Aquí  devuelvo un listado que contiene Resumenes de cobrador, en Realidad es un List<ResumenCobrador>

            [. . .]                              //Aquí el método lo acepta porque ResumenCobrador implementa IFilaListado

            return listado;

        }

    }

 

En el caso del método private List< IFilaListado > _resumen(){} no puedo decirle que sea del tipo genérico List<T> porque necesito si o si devolver un List<ResumenCobrador> en forma de List<IFilaListado>

Por lo que no puedo usar generics aquí.

 

Mi problema aquí no es que no haya encontrado la SOLUCION, pues lo solucione así, asiendo Downcasting:

  public class CobranzaMain

    {

        public List<ResumenCobrador> listadoResumenCobrador() {

            Listados lis = new Listados(new ResumenCobrador());           

            //return lis.getFilas().Select(c=>(ResumenCobrador)c).ToList();  --Esto funciona

            return lis.getFilas().Cast<ResumenCobrador>().ToList();           

        }

    }

 

El problema es que me sugirieron que no use DOWNCASTING.

El downcasting tiene un costo de procesamiento en runtime.

Casi siempre que haces downcasting, se pude decir que tenés un problema de diseño”  - Fernando Pelliccioni

 

Así que estoy buscándole la vuelta para no USAR DOWNCASTING pero no lo encuentro.

 

Mi idea es tener un objeto que me devuelva diferentes listados, llamándole a un SOLO METODO. El método “sabe” que listado devolverme porque le doy como parámetro EL OBJETO que quiero que utilice como FILAS del La colección List. Gracias al parámetro el UNICO METODO “elije” a que método privado llamar y así generarme el LISTADO.

Suena hasta poético en estos inexpertos oídos jeje, pero al querer plasmarlo en C# la realidad era otra, jeje, pero con su ayuda hasta ahora me está yendo bastante bien.

 

Si pueden darme alguna sugerencia.

 

Desde ya gracias.

 

Atte.

Néstor

Miguel Madero

unread,
Feb 26, 2013, 9:36:28 AM2/26/13
to altnet-...@googlegroups.com
Para evitar el downcasting tendriamos que hacer la clase generica, de tal forma que dependiendo del parametro generico que le pases, cambiara el parametro generico de la lista que regresas en el metodo resumen. Suena confuso yo se. Va en codigo y por partes. 


public class Listados<T>

Aqui estoy haciendo Listados una clase generica. A esto me refiero cuando decia, hacer la clase generica. 

public Listados(T fila) { //Gracias a fila el método getFilas sabe que Listado devolverme

            this._fila = fila;

        }


Cambio el constructor (y el campo _fila) para que sean del tipo T. 


public class CobranzaMain

    {

        public List<ResumenCobrador> listadoResumenCobrador() {

            var lis = new Listados(new ResumenCobrador());           

            //return lis.getFilas().Select(c=>(ResumenCobrador)c).ToList();  --Esto funciona

            return lis.getFilas().Cast<ResumenCobrador>().ToList();           

        }

    }


Aqui solo cambie Listados por var. Pude haberlo cambiado por Listados<ResumenCobrador> para no introducir otro concepto nuevo si es que no estas familiarizado con var. El resultado una vez compilado esto es lo mismo. 
A esto es a lo que me refiero cuando digo 'dependiendo del parametro generico que le pases'. En este caso, cuando utilizas la clase generica, se debe especificar que tipo de Listado es. En este caso, el compilador infiere, gracias al parametro del constructor (ResumenCobrador), cual sera el tipo de T (o el parametro generico). 

Una vez que tienes esto, puedes cambiar el tipo que retornas en getFilas para que use el parametro generico T, que en el caso anterior, regresara una List<ResumenCobrador>

 public List<T> getFilas() 




Ahora, esto podria no funcionar, porque estas regresando un IFilaListado y no T. 

                default:

                    return new List<IFilaListado>();


Asi que tendrias que cambiarlo por:

                default:

                    return new List<T>();



Ahora, toma esto unicamente como sugerencias. No conocemos la implementacion completa, pero por lo que veo estas tratando de tomar una decision diferente en base al tipo. Yo sugeriria tener diferentes clases para cada tipo que retornas en lugar de un switch case. 


Asi en lugar de tener un Listado<T> tendrias un ListadoResumenCobrador. En lugar del switch, simplemente regresas Lista<ResumenCobrador>. 




Miguel


2013/2/26 <nesto...@gmail.com>

Ale Miralles

unread,
Feb 26, 2013, 11:29:43 AM2/26/13
to altnet-...@googlegroups.com

Hola Nestor, utilizar “magic strings” en general es una mala práctica:

 

switch (this._fila.GetType().ToString()) {//Aquí decide qué TIPO de listado Retornar

    case "ResumenCobrador":

       return this._resumen();

 

Que pasa con el código anterior si haces un refactor y cambias el nombre de la clase ResumenCobrador?

Hay mucho tooling que se encarga de renombrar todas las referencias a la clase, pero  la mayoría hacen agua a la hora de reemplazar este tipo de strings. (La ultima version de R# lo resuelve bastante bien, pero algunos casos se le escapan)

 

Ahora tengo una duda, cuando decis:

“Tengo una clase listado y un método publico getFilas, que son los que me van a proveer de diferentes tipos de listados.”

Las filas de los diferentes tipos de listados, se comportan de la misma forma o cada una tiene un comportamiento especial?

Por ejemplo, cuando haces listado.GetFilas() que soles hacer con las filas que recuperas, solo lees los valores o haces algo mas?

Si además de leer los valores utilizas las filas, por ejemplo, para armar el render del reporte y cosas por el estilo, probablemente te convenga delegar la responsabilidad en la fila misma y tratarlas desde el codigo cliente como una única interfaz

Por ejemplo:

foreach (fila in report.GetFilas()){

                fila.Draw();

}

 

Te pregunto porque hace poco implemente un engine de reportes donde las filas eran responsables de proveer (a demás de los datos) información de layout (estilos por ejemplo), también tenían la posibilidad de redefinir como se renderizaban, crear enlaces a otros reportes y demás.

Si lo que estas tratando de armar es una especie de dispatcher, creo que una buena opción (como ya han mencionado) seria revisar el patrón visitor o algo similar.

Pero habría que revisar bien el caso de uso….. sobre todo como se consumen las filas desde el código cliente, para que se usan realmente….

 

 

Saludos, Ale Miralles.

http://amiralles.com.ar

nesto...@gmail.com

unread,
Feb 26, 2013, 12:40:04 PM2/26/13
to altnet-...@googlegroups.com

Que tal Ale, tienes razón y lo reemplace con if

public List<IFilaListado> getFilas() {

            if (this._fila.GetType() == typeof(ResumenCobrador)) {

                return this._resumen().Cast<IFilaListado>().ToList();

            }

            else {

                return new List<IFilaListado>();

            }

}

 

Yo uso las FILAS (clase que representa los campos de una fila, como por ejemplo ResumenCobrador) solo para cargar datos en esas FILAS y luego mostrarlas en la vista, algo así:

@model List<CobranzasLibrary.ClasesDatos.ResumenCobrador>

@{

    ViewBag.Title = "Index";

    Layout = "~/Views/Shared/_Informes.cshtml";

}

 

<h2>Index</h2>

@if (Model.Count > 0) {

    <table>

        <tr>

            <th>Cobrador</th>

            [. . .]

            <th>Momento Carga</th>

        </tr>

        @foreach (var item in @Model)

           {

             <tr>

             <td>@Html.DisplayFor(modelItem => item.cobrador)</td>       

             [. . .]  

             <td>@String.Format("{0:dd/MM/yyyy}",item.momentoCarga)</td>

             </tr>

 

           }

    </table>

}else{

   <p>No hay datos para mostrar</p>  

}

 

Es algo nuevo para mí eso de que la misma FILA se encargue de “dibujar” el html para cada uno de ellas(según entendí lo que me escribiste), pero suena interesante .

Soy nuevo con eso de los patrones así que voy a buscarme un libro de UML y patrones de diseños.

 

Desde ya gracias por tus comentarios.

 

Atte.

Néstor

Reply all
Reply to author
Forward
0 new messages