2010-02-28 15 views
6

Napisałem tę metodę rozszerzenia:Jak dynamicznie wywołać ogólną metodę rozszerzenia?

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

to działa dobrze, jeśli wywołana z rodzaju znanego w czasie kompilacji:

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

Ale jak to nazwać, jeśli typ rodzajowy nie jest znana?

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

Dlaczego rzutujesz na 'List '? Twoja 'lista' jest' List ', rzutowanie nie powiedzie się. – Vlad

+0

Ponieważ nie wie, w czasie kompilacji, jakiego rodzaju list ma: nie wie, że to 'List '. Próbuje ominąć to poprzez rzutowanie na klasę podstawową (co, jak słusznie zauważysz, nie zadziała, ponieważ 'Lista ' nie jest kompatybilna z 'Listą ' nawet jeśli 'int' jest kompatybilny z 'obiektem') . – itowlson

Odpowiedz

9

Dzieje się tak dlatego, że List<int> nie jest List<object> - typ lista nie jest kowariantna w jego parametr typu elementu. Niestety trzeba by dostać wpisane wersję metody rodzajowe i wywołać ją przy użyciu odbicia:

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

Alternatywą może być stworzenie wersji metodę rozszerzenia, które akceptuje IList zamiast IList<T>. Klasa List<T> implementuje ten interfejs nierodzajową jak również interfejs rodzajowe, dzięki czemu będą mogli zadzwonić:

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

((IList)list).WeakToDataTable(); 

(W rzeczywistości ty pewnie użyć przeciążenie zamiast inną nazwą - tylko przy użyciu inna nazwa zawołać różne rodzaje)


Więcej informacji:. W rozwiązaniu refleksji, że pomijane problem, jak określić typ elementu listy. Może to być nieco trudne, w zależności od tego, jak wyrafinowane chcesz uzyskać. Jeśli zakładamy, że obiekt będzie List<T> (dla pewnego t) to proste:

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

jeśli jesteś tylko skłonni zakładać IList<T> to jest to nieco trudniejsze, ponieważ trzeba znaleźć odpowiednie interfejs i uzyskać ogólny argument z tego. I nie możesz użyć funkcji GetInterface(), ponieważ szukasz zamkniętej instancji ogólnego interfejsu. Więc trzeba czołgać przez wszystkie interfejsy poszukuje dla jednego, który jest instancją 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]; 
    } 
} 

To będzie pracować dla pustych list, ponieważ gaśnie metadanych, a nie zawartość listy.

+0

Czy nie wystarczy przesłać do 'List ' zamiast 'List ' rozwiązać problem? – Vlad

+0

Jasne, ale pytanie zadaje pytanie "jak to nazwać **, jeśli rodzaj ogólny nie jest znany **?" (podkreślenia dodane).Stąd moja uwaga, że ​​w rzeczywistości musiałby również wymyślić listę ListEtem używając refleksji, a nie tylko założyć, że jest int. – itowlson

+0

Próbowałem tego, ale mam dwa problemy: 1. W jaki sposób uzyskać typ elementów osadzonych, jeśli lista jest pusta? 2. Mam dwie metody rozszerzenia ToDataTable(). Jak zdobyć ten dla IList ? –

0

Po problemach z uruchomieniem go z interfejsem IList<T> rozwiązałem go za pomocą interfejsu 012 zaproponowanego jako itowlson. Jest trochę brzydki ze względu na metodę _T, ale działa dobrze:

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); 
     }    
    } 
} 
Powiązane problemy