2010-02-28 16 views
6

ich diese Erweiterung Methode geschrieben:Wie wird eine generische Erweiterungsmethode dynamisch aufgerufen?

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

Es funktioniert gut, wenn mit einem Typ namens zum Zeitpunkt der Kompilierung bekannt:

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

Aber wie man es nennen, wenn der generische Typ nicht bekannt ist?

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

Warum Sie 'Liste ' werfen? Ihre "Liste" ist eine "Liste ", die Besetzung wird nicht gelingen. – Vlad

+0

Weil er zur Kompilierzeit nicht weiß, welche Art von Liste er hat: Er weiß nicht, dass es 'List ' ist. Er versucht es zu umgehen, indem er in eine Basisklasse wirft (was, wie Sie zu Recht bemerken, nicht funktionieren wird, da 'List ' nicht kompatibel mit 'List ' ist, obwohl 'int' mit' object' kompatibel ist) . – itowlson

Antwort

9

Dies geschieht, weil ein List<int> kein List<object> ist - die Liste Typ nicht in seinem Element Typparameter kovariant ist. Leider müssen Sie eine typisierte Version der generischen Methode bekommen und es mithilfe von Reflektion nennen:

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 }); 

Eine Alternative eine Version Ihrer Erweiterung Methode erstellen kann, die IList statt IList<T> akzeptiert. Die List<T> Klasse implementiert diese nicht-generische Schnittstelle sowie die generische Schnittstelle, so können Sie anrufen:

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

((IList)list).WeakToDataTable(); 

(In Wirklichkeit würden Sie vermutlich eine Überlastung, anstatt einen anderen Namen verwenden - nur mit ein anderer Name, die verschiedenen Arten ruft an)


Weitere Informationen:. In der Reflexion Lösung, übersprungen ich über das Problem, wie die Liste Elementtyp zu bestimmen. Dies kann ein bisschen schwierig sein, je nachdem, wie anspruchsvoll Sie erhalten möchten. Wenn Sie davon aus, dass das Objekt ein List<T> sein (für einige T), dann ist es einfach:

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

Wenn Sie nur bereit sind, zu übernehmen IList<T> dann ist es etwas schwieriger, weil Sie die entsprechende suchen müssen Schnittstelle und holen Sie sich das generische Argument von diesem. Und Sie können GetInterface() nicht verwenden, da Sie nach einer geschlossen konstruierten Instanz einer generischen Schnittstelle suchen. Also muss man für eine Suche aller Schnittstellen kriechen durch die eine Instanz von 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]; 
    } 
} 

Dies wird für leere Listen arbeiten, weil es aus den Metadaten geht, nicht in der Liste Inhalt.

+0

Würde nicht einfach das Casting auf 'List ' statt 'List ' das Problem lösen? – Vlad

+0

Sicher, aber die Frage fragt "wie man es nennt ** wenn der generische Typ nicht bekannt ist **?" (Betonung hinzugefügt).Daher mein Hinweis, dass er in Wirklichkeit auch den listItemType mithilfe von Reflektion herausfinden müsste, anstatt nur anzunehmen, dass es int ist. – itowlson

+0

Ich habe das versucht, aber ich habe zwei Probleme: 1. Wie bekomme ich den Typ der eingebetteten Elemente, wenn die Liste leer ist? 2. Ich habe zwei ToDataTable() Erweiterungsmethoden. Wie bekomme ich den für IList ? –

0

Nachdem ich Schwierigkeiten hatte, es mit der IList<T> Schnittstelle zu arbeiten, löste ich es mit der IList Schnittstelle wie itowlson vorgeschlagen. Es ist ein bisschen wegen der _T Methode hässlich, aber es funktioniert gut:

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); 
     }    
    } 
} 
Verwandte Themen