将任何IEnumerable分配给对象属性
本文关键字:对象 属性 分配 任何 IEnumerable | 更新日期: 2023-09-27 18:27:42
我有一个使用反射创建的对象列表,它们都是相同的类型,但在编译时类型是未知的。
我正试图找出将此列表(也使用反射)分配给对象属性的最佳方法,该对象属性可以是任何IEnumerable。
List<object>
ArrayList
Custom : List<object>
我唯一的方法是假设属性是ICollection,然后循环通过IEnumerable并添加每个项。(见下文,其中list
是IEnumerable源,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 });
}
既然你说数据是同源的,我建议你尽可能地键入它;所以假设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
实际上不是列表,而只是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];
有几种方法可以将项目添加到未知类型的现有集合中:
检查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);
}
}
我可能会选择马克的方式,因为它更安全。
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);