У меня есть устаревший код, в котором я пытаюсь сжать и удалить много повторяющихся повторов. У меня есть List<MessageItemViewModel>
в качестве параметра метода, вызываемого из действия ASP.NET MVC POST. Фактические элементы списка будут подклассом MessageItemViewModel
, например. MessageItemA1ViewModel
. В вызываемом методе этот список приводился следующим образом:
switch (level1Code)
{
case "A1":
{
var list = messages.Cast<MessageItemA1ViewModel>().ToList();
file = printHelper.Print(list).Save(exportFormat);
}
break;
case "A2":
{
var list = messages.Cast<MessageItemA2ViewModel>().ToList();
file = printHelper.Print(list).Save(exportFormat);
}
break;
case "A3":
Всего около 80 случаев, но я хочу сократить это с помощью словаря:
private readonly Dictionary<string, Type> _messageItemMap = new
Dictionary<string, Type>
{
{"A1", typeof(MessageItemA1ViewModel)},
{"A2", typeof(MessageItemA2ViewModel)},
{"A3", typeof(MessageItemA3ViewModel)},
и динамически приводить элементы списка к фактическому базовому типу. Итак, я следил за примерами SO, и подход хорошо работает, объявляя статический метод и выполняя приведение к элементу в моем списке:
public static class Ext
{
public static T Cast<T>(this object entity) where T : class
{
return entity as T;
}
}
public class ExportHelper
{
public byte[] GetFile(string level1Code, string title, List<MessageItemViewModel> messages, ExportFormat exportFormat)
{
.
.
.
var castMethod = typeof(Ext).GetMethod("Cast", BindingFlags.Static | BindingFlags.Public).MakeGenericMethod(_messageItemMap[level1Code]);
var anItemType = castMethod.Invoke(null, new object[] { messages[0] });
anItemType
правильно соответствует фактическому типу, к которому я использую. В непосредственном окне при отладке вызов anItemType is MessageItemA2ViewModel
возвращает true. НО мне нужно разыграть все предметы, поэтому я попробовал это:
var castMethod = typeof(Ext).GetMethod("Cast", BindingFlags.Static | BindingFlags.Public).MakeGenericMethod(_messageItemMap[level1Code]);
var theMessageItemModel = messages
.Select(p => castMethod.Invoke(null, new object[] { p }))
.Where(p => p != null)
.ToList();
Первоначально казалось, что это сработало, поскольку при наведении курсора на theMessageItemModel
отладка показывала значения как тип, к которому я привел, но это всего лишь магия инструментов Visual Studio. Проверка элементов в непосредственном окне, поскольку ранее элементы фактически определены как System.Object, а также я вижу, что типы возвращаемых вызовов Linq возвращают List<Object>
. Это проблема позже, потому что
theMessageItemModel
передается другому универсальному методу библиотеки, где вызывается TypeDescriptor.GetProperties(typeof(T))
, чтобы определить форму POCO для дальнейшей обработки. При передаче как List<Object>
0 свойств обнаруживается, но если я могу получить список, переданный как один из подклассов, например. List<MessageItemA1ViewModel>
, тогда он вернет свойства.
Может ли кто-нибудь предложить, как мне использовать мой Cast RuntimeMethodInfo для создания правильного списка типов типа, соответствующего level1Code в моем словаре типов?
=== Обновление === На самом деле я не упомянул одну вещь, поскольку вызываемый мной библиотечный метод является универсальным, мне пришлось отразить и это, чтобы передать динамически приведенный тип в метод Print:
var printMethod = printHelper.GetType().GetMethod("Print").MakeGenericMethod(theMessageItemModel.GetType().GetGenericArguments().Single());
var docRender = printMethod.Invoke(printHelper, new object[] { theMessageItemModel }) as IDocumentRender;
file = docRender.Save(exportFormat);
Вызов theMessageItemModel.GetType().GetGenericArguments().Single()
- это то место, где System.Object
был найден и передан из-за повторного преобразования моего вызова Linq обратно в List<System.Object>
.
Во время выполнения произошел сбой, потому что вызов нашей библиотеки не работает, потому что при вызове TypeDescriptor.GetProperties (typeof (T)) T является System.Object, и у меня есть список нулевых свойств
Но другая проблема: какой тип theMessageItemModel
будет содержать? у вас не может быть списка с разнородным типом, если это не object
, dynamic
или интерфейс или базовый класс ...
Попробуйте явно указать тип theMessageItemModel
как List<dynamic>
Хорошо, поэтому у меня есть более глубокие размышления о заявлении LINQ. Поскольку во время компиляции невозможно узнать, что возвращается из моего castMethod, тогда LINQ может разрешить только объект, поэтому мне нужно, чтобы перечисляемый список действительно мог быть разрешен при запуске. Итак, я понял, что могу вставить LINQ в свой метод Cast:
public static IEnumerable<T> CastList<T>(this IEnumerable<object> entity) where T : class
{
var ret = entity
.Select(e => e as T)
.ToList();
return ret;
}
При вызове этого он возвращает, например, List<MessageItemA2ViewModel>
, и теперь мой вызов theMessageItemModel.GetType().GetGenericArguments().Single()
дает мне тип подкласса, который фактически находится в списке, поэтому метод Print вызывается правильно:
var castListMethod = typeof(Ext).GetMethod("CastList").MakeGenericMethod(_messageItemMap[level1Code]);
var theMessageItemModel = castListMethod.Invoke(null, new object[] { messages });
var printMethod = printHelper.GetType().GetMethod("Print").MakeGenericMethod(theMessageItemModel.GetType().GetGenericArguments().Single());
var docRender = printMethod.Invoke(printHelper, new [] { theMessageItemModel }) as IDocumentRender;
file = docRender.Save(exportFormat);
и 12 свойств обнаружены с помощью TypeDescriptor.GetProperties(typeof(T))
, и в самом конце я получаю ожидаемый результат.
Но во время выполнения у вас будет правильный тип. Вероятно, немедленное окно разрешает тип компиляции, потому что метод
Invoke
возвращаетobject