2010-02-28 3 views
6

Я написал этот метод расширения:Как динамически вызывать общий метод расширения?

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

Это хорошо работает, если с названием известного типа во время компиляции:

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

Но как назвать это, если общий тип не известен?

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

Почему вы приводите в '' Список ? Ваш 'list' - это« Список ', приведение не получится. – Vlad

+0

Потому что он не знает во время компиляции, какой у него тип списка: он не знает, что это 'List '. Он пытается обойти это, отбрасывая базовый класс (который, как вы правильно заметили, не будет работать, потому что 'List ' несовместим с 'List ', даже если 'int' совместим с' object') , – itowlson

ответ

9

Это происходит потому, что List<int> не List<object> - тип списка не ковариантны в параметре типа элемента. К сожалению, вам нужно будет получить набранную версию универсального метода и назовите его с помощью отражения:

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

Альтернативы может быть, чтобы создать версию методы расширения, который принимает IList, а не IList<T>. List<T> класс реализует этот необщего интерфейс, а также общий интерфейс, так что вы будете в состоянии назвать:

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

((IList)list).WeakToDataTable(); 

(На самом деле вы, вероятно, использовать перегрузку, а не с другим именем - только с помощью другое имя, чтобы вызвать различные типы)


Дополнительная информация:. в решении отражения, я пропустил над проблемой, как определить тип элемента списка. Это может быть немного сложнее в зависимости от того, насколько вы изощренны. Если Вы предполагаете, что объект будет List<T> (для некоторого Т), то это легко:

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

Если вы только готовы взять на себя IList<T> то это немного сложнее, потому что вам нужно найти соответствующий интерфейс и получить общий аргумент от этого. И вы не можете использовать GetInterface(), потому что вы ищете закрытый сконфигурированный экземпляр универсального интерфейса. Таким образом, вы должны пресмыкаться через все интерфейсы ищет тот, который является экземпляром 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]; 
    } 
} 

Это будет работать для пустых списков, поскольку она идет от метаданных, а не содержание списка.

+0

Не просто листинг в 'List ' вместо 'List ' Решите проблему? – Vlad

+0

Несомненно, но вопрос спрашивает «как назвать это **, если общий тип неизвестен **?» (выделено курсивом).Отсюда моя заметка о том, что на самом деле ему также придется вычислять listItemType с помощью отражения, а не просто предполагать, что это int. – itowlson

+0

Я пробовал это, но у меня есть две проблемы: 1. Как я получу тип встроенных элементов, если список пуст? 2. У меня есть два метода расширения ToDataTable(). Как получить номер для IList ? –

0

После того, как у меня возникли проблемы с его работой с интерфейсом IList<T>, я решил использовать его с помощью интерфейса IList, например, itowlson. Это немного некрасиво из-за метода _t, но она хорошо работает:

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); 
     }    
    } 
} 
Смежные вопросы