2010-02-28 19 views
6

Ho scritto questo metodo di estensione:Come chiamare dinamicamente un metodo di estensione generico?

public static DataTable ToDataTable<T>(this IList<T> list) 
{...} 

Funziona bene se chiamato con un tipo noto in fase di compilazione:

DataTable tbl = new List<int>().ToDataTable(); 

Ma come chiamare se il tipo generico non è noto?

object list = new List<int>(); 
... 
tbl = Extension.ToDataTable((List<object>)list); // won't work 
+0

Perché ti rattristi a '' lista ? Il tuo 'elenco' è un' Elenco ', il cast non avrà successo. – Vlad

+0

Perché non sa al momento della compilazione quale tipo di lista ha: non sa che è "Lista ". Sta cercando di aggirare il problema passando a una classe base (che, come si nota correttamente, non funzionerà perché 'Lista ' non è compatibile con 'Elenco ' anche se 'int' è compatibile con' object') . – itowlson

risposta

9

Ciò si verifica perché un List<int> non è un List<object> - il tipo di elenco non è covariante nel suo parametro tipo di elemento. Purtroppo si avrebbe bisogno di ottenere una versione digitato del metodo generico e chiamare utilizzando la riflessione:

Type listItemType = typeof(int); // cheating for simplicity - see below for real approach 
MethodInfo openMethod = typeof(Extension).GetMethod("ToDataTable", ...); 
MethodInfo typedMethod = openMethod.MakeGenericMethod(typeof(listItemType)); 
typedMethod.Invoke(null, new object[] { list }); 

Un'alternativa potrebbe essere quella di creare una versione del metodo di estensione che accetta IList piuttosto che IList<T>. La classe List<T> implementa questa interfaccia non generica, così come l'interfaccia generica, in modo da essere in grado di chiamare:

public static DataTable WeakToDataTable(this IList list) { ... } 

((IList)list).WeakToDataTable(); 

(In realtà si sarebbe probabilmente utilizzare un sovraccarico piuttosto che un altro nome - usando solo un nome diverso per richiamare i diversi tipi)


Maggiori informazioni:. Nella soluzione riflessione, ho saltato il problema di come determinare il tipo di lista degli elementi. Questo può essere un po 'complicato a seconda di quanto sofisticato vuoi ottenere. Se stai assumendo che l'oggetto sarà un List<T> (per qualche T) allora è facile:

Type listItemType = list.GetType().GetGenericArguments()[0]; 

Se siete solo disposto ad assumere IList<T> allora è un po 'più difficile, perché è necessario individuare l'appropriata interfaccia e ottieni l'argomento generico da quello. E non puoi utilizzare GetInterface() perché stai cercando un'istanza di un'interfaccia generica costruita in modo chiuso. In modo da avere a strisciare attraverso tutte le interfacce alla ricerca di uno che è un'istanza di IList<T>:

foreach (Type itf in list.GetType().GetInterfaces()) 
{ 
    if (itf.IsGenericType && itf.GetGenericTypeDefinition == typeof(IList<>)) // note generic type definition syntax 
    { 
    listItemType = itf.GetGenericArguments()[0]; 
    } 
} 

Questo funziona per gli elenchi vuoti perché va fuori i metadati, non il contenuto dell'elenco.

+0

Non è sufficiente il cast di 'Lista ' invece di 'Lista ' risolvere il problema? – Vlad

+0

Certo, ma la domanda chiede "come chiamarla ** se il tipo generico non è noto **?" (enfasi aggiunta).Di qui la mia nota che in realtà dovrebbe anche capire la listaItemType usando il reflection piuttosto che assumere che sia int. – itowlson

+0

Ho provato questo, ma ho due problemi: 1. Come otterrò il tipo di elementi incorporati se l'elenco è vuoto? 2. Ho due metodi di estensione ToDataTable(). Come ottenere quello per IList ? –

0

Dopo aver avuto problemi a farlo funzionare con l'interfaccia IList<T> l'ho risolto utilizzando l'interfaccia IList come itowlson proposta. E 'un po' brutto a causa del metodo _T ma funziona bene:

DataTable tbl = ((IList)value).ToDataTable(); 

public static class Extensions 
{ 
    private static DataTable ToDataTable(Array array) {...} 
    private static DataTable ToDataTable(ArrayList list) {...} 
    private static DataTable ToDataTable_T(IList list) {...} 

    public static DataTable ToDataTable(this IList list) 
    { 
     if (list.GetType().IsArray) 
     { 
      // handle arrays - int[], double[,] etc. 
      return ToDataTable((Array)list); 
     } 
     else if (list.GetType().IsGenericType) 
     { 
      // handle generic lists - List<T> etc. 
      return ToDataTable_T(list); 
     } 
     else 
     { 
      // handle non generic lists - ArrayList etc. 
      return ToDataTable((ArrayList)list); 
     }    
    } 
} 
Problemi correlati