2

我有一个使用反射创建的对象列表,它们都是相同的类型,但是在编译时类型是未知的。

我试图找出将此列表(也使用反射)分配给可以是任何IEnumerable的对象属性的最佳方法。

List<object>
ArrayList
Custom : List<object>

我唯一的方法是假设属性是一个 ICollection 然后循环遍历 IEnumerable 并添加每个项目。(见下文,listIEnumerable 源在哪里,key是对象属性的字符串名称,result是对象本身)

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

2 回答 2

2

既然你说数据是同质的,我建议你尽可能地输入它;所以假设list是非空的,list[0].GetType()会告诉你Type所有的数据。此时,您可以这样做:

IList typedList = (IList)Activator.CreateInstance(
           typeof(List<>).MakeGenericType(itemType));
...
foreach(var item in list) typedListAdd(item);

或者你可以使用一个数组:

Array arr = Array.CreateInstance(itemCount, list.Count);
list.CopyTo(arr, 0);

其中任何一个都会为您提供一个类型良好的列表,这对于大多数用途(数据绑定、序列化或只是反射)来说往往效果更好。

如果list实际上不是list,而是 just IEnumerable,那么您基本上仍然可以做同样的事情,只是将创建推迟到第一项:

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];
于 2013-08-15T11:24:00.430 回答
0

有几种方法可以将项目添加到未知类型的现有集合中:

检查IList接口或检查Add方法作为后备;

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

我可能会采用 Marc 的方式,因为它更安全。

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);
于 2013-08-15T11:43:24.913 回答