2013-01-22 11 views
6

Continuo a utilizzare la funzione riportata di seguito nelle mie classi e vorrei scriverlo come generici.Uso generico di base

public static IEnumerable<MyObject> Get(string csvFile) 
{ 
    return csvFile 
     .ReadAsStream() 
     .SplitCrLf() 
     .Where(row => !string.IsNullOrWhiteSpace(row)) 
     .Select(row => new MyObject(row.Split(','))); 
} 

ho graffiare il codice qui sotto, ma non ha funzionato

public static IEnumerable<T> Get<T>(string csvFile) 
{ 
    return csvFile 
     .ReadAsStream() 
     .SplitCrLf() 
     .Where(row => !string.IsNullOrWhiteSpace(row)) 
     .Select(row => new typeof(T)(row.Split(','))); 
} 

prega di avvisare. Grazie!

+3

Non ha funzionato, in che modo? – JLRishe

+1

Presumibilmente non è stato compilato, perché 'new typeof (T)' è una sintassi non valida. – cdhowie

+0

È possibile generare un 'IEnumerable ' dal file csv. Per convertirlo in un 'IEnumerable ' è necessario definire una funzione di conversione 'Func ' e applicarla. – ja72

risposta

11

Non è possibile utilizzare new per creare istanze utilizzando tipi generici in questo modo . Prendere in considerazione la fornitura di un delegato di fabbrica per la funzione:

public static IEnumerable<T> Get<T>(string csvFile, Func<string[], T> factory) 
{ 
    return csvFile 
     .ReadAsStream() 
     .SplitCrLf() 
     .Where(row => !string.IsNullOrWhiteSpace(row)) 
     .Select(row => factory(row.Split(','))); 
} 

allora si sarebbe chiamarla in questo modo:

var myObjects = Get("file.csv", row => new MyObject(row)); 

In alternativa, è possibile restituire un IEnumerable<string[]> e lasciare che il chiamante decidere cosa fare con iT:

public static IEnumerable<string[]> Get(string csvFile) 
{ 
    return csvFile 
     .ReadAsStream() 
     .SplitCrLf() 
     .Where(row => !string.IsNullOrWhiteSpace(row)) 
     .Select(row => row.Split(',')); 
} 

Poi il chiamante potrebbe fare:

var myObjects = Get("file.csv").Select(row => new MyObject(row)); 

È possibile fornire il vincolo where T : new() e quindi è possibile creare nuove istanze che utilizzano un tipo generico, ma solo quando si fornisce un costruttore senza argomenti; non puoi fornire argomenti quando costruisci tipi generici e il tuo caso d'uso sembra richiederlo. Un delegato di fabbrica è la tua migliore opzione qui.

Per avere un riferimento, questo è il modo in costruzione con i tipi generici apparirebbe nel caso senza argomenti:

public static T Create<T>() where T : new() 
{ 
    return new T(); 
} 

Ancora meglio sarebbe un IEnumerable<IEnumerable<string>> assumendo che il vostro costruttore MyObject accetta IEnumerable<string> pure.

+0

sebbene, vi sia [Activator.CreateInstance] (http://msdn.microsoft.com/en-us/library/system.activator.createinstance.aspx). Bei suggerimenti però, +1 – Default

+2

@Default In effetti c'è. Infatti, 'new T()' è compilato come 'Activator.CreateInstance ()'. Tuttavia, si dovrebbe notare che questa tecnica (usando CreateInstance per passare argomenti a un costruttore) utilizzerà la riflessione ogni volta, incorrendo in una penalità di prestazioni potenzialmente grave. L'utilizzo di un delegato di fabbrica offre non solo maggiore flessibilità (forse si vuole fare qualcosa di più della semplice costruzione di un oggetto) sfruttando al tempo stesso il codice compilato ed evitando il costo della riflessione. – cdhowie

+0

grande aggiunta! Non lo sapevo. – Default

Problemi correlati