Невозможно создать списки с отражением в C # - PullRequest
4 голосов
/ 09 апреля 2010

У меня сейчас много проблем с Reflection в C #. Приложение, которое я пишу, позволяет пользователю изменять атрибуты определенных объектов с помощью файла конфигурации. Я хочу иметь возможность сохранить объектную модель (проект пользователя) в XML. Приведенная ниже функция вызывается в середине цикла foreach, просматривая список объектов, содержащих в себе все другие объекты проекта. Идея состоит в том, что он работает рекурсивно, чтобы перевести объектную модель в XML.

Не беспокойтесь о вызове "Unreal", который просто слегка изменяет имя объектов, если они содержат определенные слова.

      private void ReflectToXML(object anObject, XmlElement parentElement)
  {
     Type aType = anObject.GetType();
     XmlElement anXmlElement = m_xml.CreateElement(Unreal(aType.Name));
     parentElement.AppendChild(anXmlElement);
     PropertyInfo[] pinfos = aType.GetProperties();
     //loop through this objects public attributes
     foreach (PropertyInfo aInfo in pinfos)
     {
        //if the attribute is a list
        Type propertyType = aInfo.PropertyType;
        if ((propertyType.IsGenericType)&&(propertyType.GetGenericTypeDefinition() == typeof(List<>)))
        {
           List<object> listObjects = (aInfo.GetValue(anObject,null) as List<object>);
           foreach (object aListObject in listObjects)
           {
              ReflectToXML(aListObject, anXmlElement);
           }
        }
        //attribute is not a list
        else
           anXmlElement.SetAttribute(aInfo.Name, "");
     }
  }

Если атрибуты объекта являются просто строками, то они должны записывать их как строковые атрибуты в XML. Если атрибуты объектов являются списками, то он должен рекурсивно вызывать «ReflectToXML», передавая себя в качестве параметра, создавая таким образом вложенную структуру, которая мне требуется, чтобы она хорошо отражала объектную модель в памяти.

У меня проблема с линией

List<object> listObjects = (aInfo.GetValue(anObject,null) as List<object>);

Приведение не работает и просто возвращает ноль. Во время отладки я изменил строку на

object temp = aInfo.GetValue(anObject,null);

ударил точку останова, чтобы увидеть, что возвращает "GetValue". Он возвращает «Общий список объектов» Конечно, я должен быть в состоянии привести это? Раздражает то, что temp становится общим списком объектов, но поскольку я объявил temp как единственный объект, я не могу пройти через него, потому что у него нет Enumerator.

Как я могу перебрать список объектов, если у меня есть только как свойствоInfo класса?

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

Заранее спасибо

Ответы [ 4 ]

5 голосов
/ 09 апреля 2010

Я предполагаю, что фактическим значением является не List<object>, а что-то вроде List<string> или List<int> или некоторого другого типа, который не точно object?

Если это так, то причина неудачного приведения в том, что общие классы не являются ни коинвариантными, ни контравариантными.

В C # 4.0, однако, вы сможете заставить цикл foreach работать, приведя к IEnumerable<object>, потому что интерфейсы могут быть ко / контравариантными.

(намного) больше информации здесь: http://blogs.msdn.com/ericlippert/archive/tags/Covariance+and+Contravariance/default.aspx


Edit:

Думая об этом, вам не нужно общее отклонение здесь. List<T> реализует неуниверсальный IEnumerable. Это все, что вам нужно для работы цикла foreach, и вам нужны только элементы типа object, поэтому просто приведите его к IEnumerable вместо List<object>, и все должно работать нормально.

4 голосов
/ 09 апреля 2010

Обобщение и рефлексия плохо сочетаются, особенно для списков. Я бы настоятельно предложил бы использовать (неуниверсальный) IList или, если это не удалось (некоторые универсальные списки не реализуют это), IEnumerable (начиная с IEnumerable<T> : IEnumerable) и вызвать Add вручную.

Я действительно чувствую вашу боль (я поддерживаю API сериализации OSS).

1 голос
/ 09 апреля 2010

В C # 3 вы не можете разыграть Lists<T> на другие типы Lists<T>, даже если вы разыгрываете что-то вроде List<Object>. Это явно не разрешено даже при приведении к списку.

В 4.0 разница немного меняется с интерфейсами с добавлением ключевых слов in и out .

Вот ссылка на Эрика Липперта, объясняющая, как и почему это так.

http://blogs.msdn.com/ericlippert/archive/tags/Covariance+and+Contravariance/default.aspx

0 голосов
/ 09 апреля 2010

Не могли бы вы просто привести их к объектам с помощью OfType ()?

List<object> listObjects = anObject.OfType<object>().ToList();

Обновление:

Если бы требование не было для List<object>, это также сработало бы и не требовало бы дублирования пунктов списка:

var enumerableObjects = anObject.OfType<object>();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...