Как назвать универсальный дополнительный метод динамично?

Я записал этот дополнительный метод:

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
7
задан itowlson 28 February 2010 в 00:34
поделиться

2 ответа

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

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 реализует этот неуниверсальный интерфейс, а также общий интерфейс, поэтому вы сможете вызвать:

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

((IList)list).WeakToDataTable();

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


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

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

Если вы готовы только предположить IList , то это немного сложнее, потому что вам нужно найти соответствующий интерфейс и получить от него общий аргумент. И вы не можете использовать GetInterface (), потому что ищете закрытый созданный экземпляр универсального интерфейса. Таким образом, вы должны проползти через все интерфейсы в поисках того, который является экземпляром IList :

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

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

11
ответ дан 6 December 2019 в 23:04
поделиться

После проблем с работой с интерфейсом IList я решил эту проблему с помощью 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);
        }            
    }
}
0
ответ дан 6 December 2019 в 23:04
поделиться
Другие вопросы по тегам:

Похожие вопросы: