如何通过反射将IEnumerable<T>通用复制到IList<T>?
通用解决IEnumerable到泛型集合的反射复制方案
问题背景
在通过反射复制对象时,源对象属性为IEnumerable<T>类型(如string[]),目标对象对应属性为IList<T>类型,直接赋值会抛出类型转换异常;使用Convert.ChangeType也因未实现IConvertible接口失败,硬编码特定类型的转换方案扩展性差,需要通用的解决方法。
通用解决方案
通过反射动态识别集合类型,调用泛型方法或构造函数完成转换,以下是完整实现:
1. 辅助转换方法
该方法负责将IEnumerable<T>类型的源值转换为目标集合类型:
private object ConvertToTargetCollection(object sourceValue, Type targetType) { if (sourceValue == null) return null; // 排除string(string虽实现IEnumerable<char>但无需转换) if (typeof(string).IsAssignableFrom(sourceValue.GetType())) return sourceValue; // 检查源是否为泛型IEnumerable var sourceEnumerableType = sourceValue.GetType().GetInterface(typeof(IEnumerable<>).FullName); if (sourceEnumerableType == null) return sourceValue; // 检查目标是否为泛型类型 if (!targetType.IsGenericType) return sourceValue; var targetGenericArg = targetType.GetGenericArguments()[0]; var sourceGenericArg = sourceEnumerableType.GetGenericArguments()[0]; // 确保泛型参数类型兼容 if (!targetGenericArg.IsAssignableFrom(sourceGenericArg)) return sourceValue; // 优先尝试使用目标类型的IEnumerable<T>构造函数(支持自定义集合) var constructor = targetType.GetConstructor(new[] { sourceEnumerableType }); if (constructor != null) { return constructor.Invoke(new[] { sourceValue }); } // fallback 到Enumerable.ToList<T>(适用于List<T>、IList<T>等) var toListMethod = typeof(Enumerable).GetMethod(nameof(Enumerable.ToList)) .MakeGenericMethod(targetGenericArg); return toListMethod.Invoke(null, new[] { sourceValue }); }
2. 修改后的复制方法
在原CopyTo方法中替换硬编码逻辑,调用上述辅助方法:
public T CopyTo<T>(object src) where T : new() { var targetObj = new T(); var sourceType = src.GetType(); BindingFlags flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.SetProperty; var sourcePi = sourceType.GetProperties(flags); foreach (var property in sourcePi) { var pi = targetObj.GetType().GetProperty(property.Name); if (pi == null || !pi.CanWrite) continue; object sourceValue = property.GetValue(src, null); // 应用通用集合转换 sourceValue = ConvertToTargetCollection(sourceValue, pi.PropertyType); pi.SetValue(targetObj, sourceValue, null); } return targetObj; }
方案说明
- 兼容性处理:排除string类型,避免误转换;检查泛型参数兼容性,确保类型可以安全转换。
- 灵活性:优先使用目标集合类型的
IEnumerable<T>构造函数,支持自定义泛型集合;如果没有对应构造函数,则使用Enumerable.ToList<T>生成List<T>(可赋值给IList<T>、ICollection<T>等接口类型)。 - 无硬编码:无需指定具体的
T类型,通过反射动态适配任意泛型集合转换需求。
原问题报错原因
- 直接赋值失败:
string[]是数组类型,与List<string>属于不同类型,无法直接隐式转换。 Convert.ChangeType失败:数组和泛型集合未实现IConvertible接口,该方法仅支持基础类型的转换。
内容的提问来源于stack exchange,提问作者M.Hassan
相关产品推荐
相关产品推荐

