重构遗留代码:将Object类型的List<T>转换为List<U>的实现求助
问题分析与修正方案
现有代码的核心问题
MakeGenericType未接收返回值:原代码中listType.MakeGenericType(...)的结果没有赋值回变量,导致listType始终是开放泛型的List<>,而非具体的List<T>,直接引发Convert.ChangeType失败。- 泛型方法调用不匹配:
ConvertListToProxy是泛型方法,但调用时传入的newVal是object类型,无法自动推断泛型参数,导致运行时类型错误。 - TypeToProxy字典key错误:原代码用
value.GetType()(即List的类型)作为字典key,但字典的映射逻辑是 T→U(元素类型映射),应该用List的泛型参数类型作为key。 - PropertyCopier冗余参数:
CopyFrom方法中的target参数未被使用,方法内部自行创建了U实例,属于冗余设计且易造成误解。
修正后的代码实现
1. 优化PropertyCopier类
public static class PropertyCopier { // 移除冗余的target参数,添加new()约束简化实例创建 public static U CopyFrom<T, U>(T source) where U : new() { var targetObj = new U(); var targetType = typeof(U); var sourceType = typeof(T); foreach (var targetProp in targetType.GetProperties()) { // 优先匹配属性 var sourceProp = sourceType.GetProperty(targetProp.Name); if (sourceProp != null) { targetProp.SetValue(targetObj, sourceProp.GetValue(source)); continue; } // 属性不存在时匹配字段 var sourceField = sourceType.GetField(targetProp.Name); if (sourceField == null) { throw new InvalidOperationException($"未在类型 {sourceType.Name} 中找到名为 {targetProp.Name} 的属性或字段"); } targetProp.SetValue(targetObj, sourceField.GetValue(source)); } return targetObj; } }
2. 修正CustomTypeConverter的核心方法
private Dictionary<Type, Type> TypeToProxy = new Dictionary<Type, Type>(); // 兼容更多List子类(可选,若仅处理原生List<>可保留原逻辑) private bool isGenericList(Type type) { return type.IsGenericType && typeof(IList<>).IsAssignableFrom(type.GetGenericTypeDefinition()); } private Type GetFirstArgumentType(Type type) { return type.GetGenericArguments()[0]; } // 移除冗余的target参数,依赖new()约束创建目标实例 private List<U> ConvertListToProxy<T, U>(List<T> source) where U : new() { return source.Select(x => PropertyCopier.CopyFrom<T, U>(x)).ToList(); } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { // ... 其他序列化逻辑 ... if (isGenericList(value.GetType())) { var sourceListType = value.GetType(); var sourceElementType = GetFirstArgumentType(sourceListType); // 检查是否存在元素类型映射 if (!TypeToProxy.TryGetValue(sourceElementType, out var targetElementType)) { // 无映射时直接序列化原列表,避免崩溃 serializer.Serialize(writer, value); return; } // 通过反射调用泛型转换方法(解决object类型无法推断泛型参数的问题) var convertMethod = typeof(CustomTypeConverter) .GetMethod(nameof(ConvertListToProxy), BindingFlags.NonPublic | BindingFlags.Instance) .MakeGenericMethod(sourceElementType, targetElementType); var targetList = convertMethod.Invoke(this, new[] { value }); // 序列化转换后的目标列表 serializer.Serialize(writer, targetList); } // ... 其他序列化逻辑 ... }
关键修正说明
- 反射调用泛型方法:由于输入的
value是object类型,无法直接推断ConvertListToProxy的泛型参数,因此通过反射获取泛型方法定义,构造具体的T和U类型后调用。 - 修正字典映射逻辑:改为用List的元素类型作为字典key,符合
T→U的映射需求。 - 增加异常安全处理:添加
TryGetValue检查映射是否存在,避免无映射时抛出异常,提升代码鲁棒性。 - 优化实例创建:利用
where U : new()约束替代Activator.CreateInstance,提高性能和代码可读性。
内容的提问来源于stack exchange,提问作者Mustafa şentürk
相关产品推荐
相关产品推荐

