Assign any IEnumerable to object property

2019-07-23 18:27发布

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

标签: c# reflection
2条回答
女痞
2楼-- · 2019-07-23 18:59

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];
查看更多
我只想做你的唯一
3楼-- · 2019-07-23 19:03

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);
查看更多
登录 后发表回答