C#运行时利用反射合并集合包装类List<T>属性问题求解
运行时动态合并泛型集合包装类实现
场景说明
当前需要处理封装了List<T>集合的自定义类,示例类结构如下:
public class CollA { public List<SomeType> Items {get;set;} // 其他不相关属性 }
已知约束:所有这类集合包装类有且仅有一个List<T>类型的属性。
需求为:传入多个同类型包装类实例,返回一个全新的同类型实例,其集合属性包含所有传入实例集合属性的元素合集,预期效果如下:
// 注:原示例对象初始化语法存在笔误,已修正 var A = new CollA{ Items = new List<SomeType> { new SomeType("A"), new SomeType("B") }}; var B = new CollA{ Items = new List<SomeType> { new SomeType("C"), new SomeType("D") }}; var result = SomeMythicalCombine<CollA>(A, B); // result.Items 按顺序包含:new SomeType("A"), new SomeType("B"), new SomeType("C"), new SomeType("D")
由于类型只有运行时才能确定,无法在编译期写死特定类型的处理逻辑,已通过反射写出部分实现代码:
public T SomeMythicalCombine<T>(params object[] collections) { var collectionType = typeof(T); var listProperty = collectionType.GetProperties() .Single(p=> typeof(IList).IsAssignableFrom(p.PropertyType)); var listPropertyName = listProperty.Name; var result = Activator.CreateInstance(collectionType); var innerType = listProperty.PropertyType.GenericTypeArguments[0]; var listType = typeof(List<>).MakeGenericType(innerType); var list = Activator.CreateInstance(listType); foreach(var collection in collections) { var listValues = collection.GetType().GetProperty(listPropertyName).GetValue(collection); // 逻辑缺口:listValues为object类型,需要转为可遍历结构,调用(list as IList).Add()完成元素添加 } result.GetType().GetProperty(listPropertyName).SetValue(result, list); return result as T; }
待补全逻辑:PropertyInfo.GetValue()返回的listValues是object类型,需要转为可遍历类型,逐个将元素加入新创建的列表中完成合并。
实现方案
不需要将listValues强转为具体的泛型List<T>类型。所有List<T>都实现了非泛型System.Collections.IEnumerable接口,直接将listValues转为该非泛型接口即可完成遍历;同时新创建的list转为非泛型IList后,Add方法接收object类型参数,无需感知具体泛型类型就可以直接传入遍历到的元素。
补全后的核心代码如下:
// 提前将新列表转为非泛型IList,避免循环内重复转换 IList resultList = list as IList; foreach(var collection in collections) { // 跳过空入参避免空引用异常 if (collection == null) continue; var listValues = collection.GetType().GetProperty(listPropertyName).GetValue(collection); // 跳过集合属性为null的实例 if (listValues == null) continue; // 直接转非泛型IEnumerable遍历,不需要知道具体元素类型 foreach (var item in listValues as System.Collections.IEnumerable) { resultList.Add(item); } }
可选优化点
- 默认的
GetProperties()方法仅查找公共实例属性,如果存在非公共属性场景,可以传入绑定标记扩展查找范围:GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) - 增加入参类型校验,确保传入的所有collections都与泛型T类型一致,避免运行时找不到对应属性抛出异常
- 如果有去重需求,可以在所有元素添加完成后,通过反射调用对应泛型的
Distinct方法处理,纯合集场景直接调用Add即可
内容的提问来源于stack exchange,提问作者Stuart Hemming
相关产品推荐
相关产品推荐

