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
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:
Saludos, Ale Miralles
--
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.
var result1 = s.Select(c => (ResumenCobrador)c);
--
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
Gracias!!, tu sugerencia también me funciono pero agregándole .ToList
lis.getFilas().Cast<ResumenCobrador>().ToList();
Atte.
Nestor
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.
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
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
public Listados(T fila) { //Gracias a fila el método getFilas sabe que Listado devolverme
this._fila = fila;
}
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();
}
}
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>.
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.
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