Esto debería ser simple, pero sigo teniendo problemas con esto. Tengo una clase genérica Csvs<T> con un método que devuelve un tipo List<T> a través de una Propiedad llamada Dockets. (El método lee archivos CSV y devuelve la lista de registros en ese archivo CSV. T es el tipo de ese archivo CSV, que se define en una serie de modelos, que contienen la fila de encabezado como Propiedades)

En mi clase de llamada (que no puedo hacer genérico), tengo una declaración de cambio basada en un enum que contiene los nombres de las clases que uso en T.

switch (_userInput.CsvType)
            {
                case CsvType.Type1:
                    var csvType1 = new Csvs<Type1>(_userInput);
                    _dockets  = csvType1.Dockets;                        
                    break;
                case CsvType.Type2:
                    var csvType2 = new Csvs<Type2>(_userInput);
                    _dockets  = csvType2.Dockets;                        
                    break;
                case CsvType.Type3:
                    var csvType3 = new Csvs<Type3>(_userInput);
                    _dockets  = csvType3.Dockets;                        
                    break;
                // more cases here [...]
                default:
                    throw new ArgumentOutOfRangeException();
            }

(Sería bueno si pudiera encontrar una mejor manera que usar este interruptor también, pero funciona por ahora).

¿Qué no funciona, o lo que no sé cómo hacer es cómo declarar _dockets? Dado que T podría ser cualquiera de los tipos. Intenté extraer una interfaz ICsvs y hacer que la clase la herede, y luego declare

var _dockets = new List<ICsvs>;

Pero esto arroja un error, que no puedo convertir implícitamente el List<Type1> en un List<ICsvs>.

Y no puedo enviar la Lista a un tipo List<ICsvs>. (Entonces tengo la próxima excepción)

No se puede convertir el tipo 'System.Collections.Generic.List' a 'System.Collections.Generic.List' a través de una conversión de referencia, conversión de boxeo, conversión de unboxing, conversión de ajuste o conversión de tipo nulo

Lo mismo sucede si uso una clase abstracta.

Entonces, ¿cómo almaceno la Lista devuelta en la clase que llama? ¿Cómo declaro un campo o propiedad que puede contener cualquiera de los tipos?

2
4ndy 16 jun. 2017 a las 13:47

3 respuestas

La mejor respuesta

Creo que por lo que estaba buscando, es decir, almacenar el valor de retorno para su uso posterior, dynamic es la mejor respuesta (Gracias @Rahul). Me puedo preocupar por el tipo más tarde. Gracias por toda tu ayuda.

dynamic _dockets;

Luego cambie el caso a través de las devoluciones.

0
4ndy 16 jun. 2017 a las 11:19

La forma más sencilla de lograr esto es hacer que todos sus tipos implementen ICsv, de esta manera:

interface ICsv { }

class CsvType1 : ICsv { }

class CsvType2 : ICsv { }

class Csvs<TCsvModel> 
   where TCsvModel: ICsv
{
    public Csvs(IList<TCsvModel> csvModel)
    {
       this.Dockets = csvModel;
    }

    public IList<TCsvModel> Dockets { get; private set;}
}

Entonces puedes usar esto como:

IEnumerable<ICvs> dockets;

var cvsType1 = new Csvs<CsvType1>(_input);
dockets = cvsType1.Dockets

var cvsType2 = new Csvs<CsvType2>(_input);
dockets = cvsType2.Dockets
1
vdefeo 16 jun. 2017 a las 11:21

Lo más significativo / polimórfico que puedes hacer es hacer que la propiedad Dockets devuelva una interfaz que describa lo que haces con ellos. Dependiendo de sus requisitos, eso puede o no ser factible, pero tiene poca otra opción. Como comenté, los genéricos realmente solo funcionan bien cuando se conoce el tipo en tiempo de compilación.

A modo de ejemplo. Una interfaz

public interface IDockets
{
    void DoSomethingWithRecords();
}

Una implementación genérica que se devolverá de su clase Csvs

public class Dockets<T> : IDockets
{
    private IEnumerable<T> dockets;
    public Dockets(IEnumerable<T> dockets)
    {
      this.dockets = dockets;
    }
    public void DoSomethingWithRecords()
    {
        foreach(var docket in dockets)
             Console.WriteLine(docket); // do whatever
    }
} 

Esta clase luego sería devuelta desde su propiedad Csvs clase Dockets

public class Csvs<T>
{
    private List<T> parsedDockets; // assume you have something like this:
    public IDockets Dockets{ get { return new Dockets(parsedDockets); } }
}

Y tu código de llamada

IDockets dockets = null;
switch (_userInput.CsvType)
{
    case CsvType.Type1:
        var csvType1 = new Csvs<Type1>(_userInput);
        dockets  = csvType1.Dockets;                        
        break;
   // Snip //
}
dockets.DoSomethingWithRecords();
0
Jamiec 16 jun. 2017 a las 11:11