I have a list of objects created using reflection, they are all the same type, however the type is unknown at compile time.
I'm trying to figure out the best way of assigning this list (also using reflection) to an object property which could be any IEnumerable.
List<object>
ArrayList
Custom : List<object>
The only approach I have is to assume the property is an ICollection then loop through the IEnumerable and add each item. (See below, where list
is the IEnumerable source, key
is the string name of the object property and result
is the object itself)
foreach (object item in list) {
PropertyInfo prop = result.GetType().GetProperty(key);
var collection = prop.GetValue(result, null);
Type collectionType = collection.GetType();
MethodInfo add = collectionType.GetMethod("Add", BindingFlags.Public | BindingFlags.Instance);
add.Invoke(collection, new object[] { item });
}
Since you say the data is homogeous, I would suggest typing it as closely as you can; so assuming list
is non-empty, list[0].GetType()
will tell you the Type
of all the data. At this point, you could do:
IList typedList = (IList)Activator.CreateInstance(
typeof(List<>).MakeGenericType(itemType));
...
foreach(var item in list) typedListAdd(item);
or you could use an array:
Array arr = Array.CreateInstance(itemCount, list.Count);
list.CopyTo(arr, 0);
Either of which will give you a well typed list, which tends to work much better for most purposes (data-binding, serialization, or just reflection).
If list
is not actually a list, but is just IEnumerable
, then you can basically still do the same thing, but simply defer the creation until the first item:
IList typedList = null;
foreach(object item in list) {
if(typedList == null) {
typedList = (IList)Activator.CreateInstance(
typeof(List<>).MakeGenericType(item.GetType()));
}
typedList.Add(item);
}
return typedList ?? new object[0];
There are a couple of ways that you can add items to an existing collection of not known type:
Check for the IList
interface or check of an Add
method as a fallback;
public void Add(object obj, string propertyName, IEnumerable enumerable)
{
Action<object> add;
PropertyInfo prop = obj.GetType().GetProperty(propertyName);
var property = prop.GetValue(obj, null);
var collection = property as IList;
// Check for IList
if(collection != null)
{
add = item => collection.Add(item);
}
// Try to get an Add method as fallback
else
{
var objType = obj.GetType();
var addMethod = objType.GetMethod("Add", BindingFlags.Public | BindingFlags.Instance);
// Property doesn't support Adding
if(addMethod == null) throw new InvalidOperationException("Method Add does not exist on class " + objType.Name);
add = item => addMethod.Invoke(obj, new object[] { item });
}
foreach (var item in enumerable)
{
add(item);
}
}
I would probably go with Marc's way since it's more type safe.
public class Foo
{
public Foo()
{
Bar = new List<string>();
}
public List<string> Bar { get; set; }
public string Qux { get; set; }
}
var result = new Foo();
var key = "Bar";
var list = new List<object> { "A", "B" };
Add(result, key, list);