views:

170

answers:

3

Lets say I have this class

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

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

I'm working on a deserializer of sorts and I want to be able to create and populate the Child list from the data retrieved. I've gotten this far (I've cut out a lot of handling for other types for this example so its unnecessarily "iffy" but bare with me):

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

        }
    }
}

I can't figure out how to cast list to essentially List<t.GetType()> so I can access the add method and add the item.

+3  A: 

I would say you should cast down to System.Collections.IList and directly call the Add method. Pros: simple, no ugly reflection. Cons: causes boxing for Lists containing value types.

bobbymcr
I like that idea.
John Sheehan
+6  A: 

This should work unless I am really missing something.

Outside of the loop

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

Inside the loop

add.Invoke(list, new[] { item });
ChaosPandion
This is a good answer and I appreciate your response so +1. bobbymcr's works though and doesn't use reflection so I'm going with that. Thanks again.
John Sheehan
A: 

You need to call type.MakeGenericType(type.GetGenericArguments()), which will return the correct closed generic type. You would then need to reflectively call the Add method. The code should look something like this:

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

I'm not sure what you would use as instance, but this is the actual object on which the method will be invoked.

The better question to ask is why are you trying to do this? The .NET Framework already includes serializers that know how to do all of this type of work. If you can, you should be using one of those instead of trying to "roll your own".

Scott Dorman
I knew someone would ask "why?" XmlSerializer has some limitations (doesn't support List<T> most notably) and the other options I explored were too verbose for this project (which is going to be a framework, so if it operates at a little lower level, it's fine with me). Basically in order to achieve simplicity I'm looking for, I have to roll my own.
John Sheehan
@John Sheehan: Why is the resulting serialized data intersting/important to you? There are other serializers available than just XmlSerializer, such as the WCF data contract serializer. It can easily be used and supports `List<T>`. Yes, it can generate some ugly looking XML, but in general that shouldn't matter.
Scott Dorman
I don't need an serializing, only deserializing and I don't want to use any attributes.
John Sheehan