2009-10-10 1 views
14

Допустим, у меня есть этот классДинамическое добавление элементов в список <T> посредством отражения

class Child { 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
} 

class Container { 
    public List<Child> { get; set; } 
} 

Я работаю на десериализатор рода, и я хочу, чтобы иметь возможность создавать и заполнять Child список из данных извлекаться. Я получил это далеко (я вырезал много обработки для других типов для данного примера поэтому его излишне «ненадежный», но чуть-чуть со мной):

var props = typeof(Container).GetProperties(); 

foreach (var prop in props) { 
    var type = prop.PropertyType; 
    var name = prop.Name; 

    if (type.IsGenericType) { 
     var t = type.GetGenericArguments()[0]; 

     if (type == typeof(List<>)) { 
      var list = Activator.CreateInstance(type); 
      var elements = GetElements(); 

      foreach (var element in elements) { 
       var item = Activator.CreateInstance(t); 
       Map(item, element); 

       // ??? how do I do list.Add(item) here? 
      } 

      prop.SetValue(x, list, null); // x is an instance of Container 

     } 
    } 
} 

Я не могу понять, как бросить list по существу List<t.GetType()>, поэтому я могу получить доступ к методу добавления и добавить элемент.

ответ

19

Я бы сказал, что вы должны отказаться от System.Collections.IList и напрямую вызвать метод Добавить. Плюсы: просто, не уродливое отражение. Минусы: вызывает бокс для списков, содержащих типы значений.

+0

Мне нравится эта идея. –

17

Это должно работать, если я действительно ничего не пропустил.

Вне контура

var add = type.GetMethod("Add"); 

Внутри цикла

add.Invoke(list, new[] { item }); 
+0

Это хороший ответ, и я ценю ваш ответ так +1. Работы bobbymcr хотя и не используют отражение, поэтому я собираюсь с этим. Еще раз спасибо. –

1

Вам нужно позвонить type.MakeGenericType(type.GetGenericArguments()), который будет возвращать правильный закрыт общий тип. Затем вам необходимо будет рефлексивно вызвать метод Add. Код должен выглядеть следующим образом:

Type listType = type.MakeGenericType(type.GetGenericArguments()); 
    MethodInfo addMethod = listType.GetMethod("Add"); 
    addMethod.Invoke(instance, new object[] { ... }); 

я не уверен, что вы могли бы использовать в качестве instance, но это реальный объект, на котором будет вызван метод.

Лучше спросить, почему вы пытаетесь это сделать? В .NET Framework уже есть сериализаторы, которые знают, как выполнять весь этот вид работы. Если вы можете, вы должны использовать один из них вместо того, чтобы пытаться «сворачивать свои собственные».

+0

Я знал, что кто-то спросит «почему?» XmlSerializer имеет некоторые ограничения (в особенности не поддерживает список ), а другие параметры, которые я изучил, были слишком подробными для этого проекта (который будет представлять собой структуру, поэтому, если он работает на чуть более низком уровне, все в порядке со мной) , В принципе, чтобы добиться простоты, которую я ищу, я должен сворачивать самостоятельно. –

+0

@John Sheehan: Почему результирующие сериализованные данные интересуются/важны для вас? Существуют и другие сериализаторы, кроме XmlSerializer, такие как сериализатор данных WCF. Его можно легко использовать и поддерживает «Список ». Да, он может генерировать некоторые уродливые XML, но в целом это не имеет значения. –

+0

Мне не нужна сериализация, только десериализация, и я не хочу использовать какие-либо атрибуты. –

0

это то, что я делаю

var genericType = _primaryType.GetGenericArguments().First(); 
    var type = typeof(List<>).MakeGenericType(genericType); 
    IList o = (IList)Activator.CreateInstance(type); 
    o.Add(x)