You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过反射将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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 00:05:38