Получить типы с помощью IEnumerable GetGenericArguments
Я разработал помощник MVC для создания отображаемых и редактируемых таблиц (требуется плагин jquery для динамического добавления и удаления строк с полной обратной передачей в редактируемых таблицах), например
@Htm.TableDisplayFor(m => m.MyCollection as ICollection)
который используется вместе с атрибутами, будет включать итоги в нижнем колонтитуле, добавлять столбцы для просмотра и редактирования ссылок, отображать гиперссылки для сложного типа и т. д., например
[TableColumn(IncludeTotals = true)]
Я собираюсь опубликовать его на CodeProject, но перед этим хотел бы решить одну проблему. Помощник первым получает ModelMetadata
из выражения проверяет, что он реализует ICollection
, затем получает тип в коллекции (обратите внимание, что следующий фрагмент взят из принятых ответов на SO, но, как объясняется ниже, не совсем корректен)
if (collection.GetType().IsGenericType)
{
Type type = collection.GetType().GetGenericArguments()[0]
Тип используется для генерации ModelMetadata
для заголовка таблицы (в таблице может не быть каких-либо строк) и каждой строки в теле таблицы (в случае, если некоторые элементы являются унаследованными типами, которые имеют дополнительные свойства и в противном случае могут испортить расположение столбцов)
foreach (var item in collection)
{
ModelMetadata itemMetadata = ModelMetadataProviders.Current
.GetMetadataForType(() => item, type);
То, что я хотел бы иметь возможность использовать это IEnumerable
скорее, чем ICollection
чтобы .ToList()
не нужно вызывать выражения linq.
В большинстве случаев IEnumerable
отлично работает например
IEnumerable items = MyCollection.Where(i => i....);
хорошо, потому что .GetGenericArguments()
возвращает массив, содержащий только один тип. Проблема в том, что.GetGenericArguments() в некоторых запросах возвращает 2 или более типов, и, похоже, логического порядка нет. Например
IEnumerable items = MyCollection.OrderBy(i => i...);
возвращает [0] тип в коллекции и [1] тип, используемый для упорядочивания.
В этом случае .GetGenericArguments()[0]
все еще работает, но
MyCollection.Select(i => new AnotherItem()
{
ID = i.ID,
Name = 1.Name
}
возвращает [0] тип в исходной коллекции и [1] тип AnotherItem
Так .GetGenericArguments()[1]
это то, что мне нужно для рендеринга таблицы AnotherItem
,
У меня вопрос, есть ли надежный способ использования условных операторов для получения типа, который мне нужен для отображения таблицы?
Из моих тестов до сих пор, используя .GetGenericArguments().Last()
работает во всех случаях, кроме случаев использования OrderBy()
потому что ключ сортировки - последний тип.
Несколько вещей, которые я пробовал до сих пор, включают игнорирование типов, которые являются типами значений (как это часто бывает с OrderBy()
, но OrderBy()
запросы могут использовать string
(что может быть проверено) или, что еще хуже, класс, который перегружает операторы ==, <и> (в этом случае я не смогу определить, какой тип является правильным), и я не смог найти способ проверить если коллекция реализует IOrderedEnumerable
,
1 ответ
Решено (используя комментарии Chris Sinclair)
private static Type GetCollectionType(IEnumerable collection)
{
Type type = collection.GetType();
if (type.IsGenericType)
{
Type[] types = type.GetGenericArguments();
if (types.Length == 1)
{
return types[0];
}
else
{
// Could be null if implements two IEnumerable
return type.GetInterfaces().Where(t => t.IsGenericType)
.Where(t => t.GetGenericTypeDefinition() == typeof(IEnumerable<>))
.SingleOrDefault().GetGenericArguments()[0];
}
}
else if (collection.GetType().IsArray)
{
return type.GetElementType();
}
// TODO: Who knows, but its probably not suitable to render in a table
return null;
}