Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unable to cast lists with Reflection in C#

I am having a lot of trouble with Reflection in C# at the moment. The app I am writing allows the user to modify the attributes of certain objects using a config file. I want to be able to save the object model (users project) to XML. The function below is called in the middle of a foreach loop, looping through a list of objects that contain all the other objects in the project within them. The idea is, that it works recursively to translate the object model into XML.

Dont worry about the call to "Unreal" that just modifes the name of the objects slightly if they contain certain words.

      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, "");
     }
  }

If an object attributes are just strings then it should be writing them out as string attributes in the XML. If an objects attributes are lists, then it should recursively call "ReflectToXML" passing itself in as a parameter, thereby creating the nested structure I require that nicely reflect the object model in memory.

The problem I have is with the line

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

The cast doesn't work and it just returns null. While debugging I changed the line to

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

slapped a breakpoint on it to see what "GetValue" was returning. It returns a "Generic list of objects" Surely I should be able to cast that? The annoying thing is that temp becomes a generic list of objects but because i declared temp as a singular object, I can't loop through it because it has no Enumerator.

How can I loop through a list of objects when I only have it as a propertyInfo of a class?

I know at this point I will only be saving a list of empty strings out anyway, but thats fine. I would be happy to see the structure save out for now.

Thanks in advance

like image 332
DrLazer Avatar asked Dec 29 '22 20:12

DrLazer


1 Answers

I'm assuming that the actual value is not a List<object> but is something like a List<string> or List<int> or some other type that isn't exactly object?

If so, then the reason that the cast is failing is because generic classes are neither co- nor contravariant.

In C# 4.0, however, you will be able to make the foreach loop work by casting to IEnumerable<object> because interfaces can be co/contravariant.

(Much) more information here: http://blogs.msdn.com/ericlippert/archive/tags/Covariance+and+Contravariance/default.aspx


Edit:

Thinking about it, you don't need generic variance here. List<T> implements the non-generic IEnumerable. This is all you need for the foreach loop to operate, and you only need the elements as type object so just cast it to an IEnumerable instead of List<object> and everything should work fine.

like image 111
Greg Beech Avatar answered Dec 31 '22 14:12

Greg Beech