如何将List<KeyValuePair<string,string>>转换为自定义实体类?解决可空类型问题
问题分析与解决方案
首先,你的代码存在两个核心问题:
- 错误获取属性类型:你使用了
pro.GetType(),这会返回PropertyInfo类型,而非属性本身的类型。正确的做法是使用pro.PropertyType。 - Nullable 类型判断逻辑错误:
- 对于引用类型(如
string),string?只是编译时的可空注解,运行时它和string是同一个类型,因此typeof(string?)等价于typeof(string),无法通过typeof区分。 - 对于值类型(如
int),int?是Nullable<int>的语法糖,运行时是一个独立的泛型类型,所以typeof(int?)能正确识别。
- 对于引用类型(如
接下来是修复后的完整代码:
private T GetItem<T>(List<KeyValuePair<string, string>> item) where T : new() { Type modelType = typeof(T); T obj = new T(); // 替换Activator.CreateInstance<T>(),更简洁且有编译时检查 foreach (PropertyInfo prop in modelType.GetProperties()) { // 查找对应Key的值 var kvp = item.FirstOrDefault(x => x.Key.Equals(prop.Name, StringComparison.OrdinalIgnoreCase)); string? val = kvp.Value; // 处理API返回的"NULL"字符串 if (string.Equals(val, "NULL", StringComparison.OrdinalIgnoreCase)) val = null; Type propType = prop.PropertyType; // 处理字符串类型(包括string?) if (propType == typeof(string)) { prop.SetValue(obj, val); continue; } // 处理可空值类型(如int?) Type? underlyingType = Nullable.GetUnderlyingType(propType); if (underlyingType != null) { if (val == null) { prop.SetValue(obj, null); } else if (TryParseValue(val, underlyingType, out object? parsedValue)) { prop.SetValue(obj, parsedValue); } else { prop.SetValue(obj, null); } continue; } // 处理非可空值类型(如int) if (propType.IsValueType) { if (TryParseValue(val, propType, out object? parsedValue)) { prop.SetValue(obj, parsedValue); } else { // 解析失败时设置类型默认值(如int默认0) prop.SetValue(obj, Activator.CreateInstance(propType)); } } } return obj; } // 通用解析方法,支持基础值类型 private bool TryParseValue(string? input, Type targetType, out object? result) { result = null; if (input == null) return false; try { result = Convert.ChangeType(input, targetType); return true; } catch (FormatException) { return false; } }
关键改进点说明:
- 替换类型获取方式:用
prop.PropertyType替代pro.GetType(),正确获取属性的实际类型。 - 统一处理字符串可空:直接判断
propType == typeof(string),因为string?在运行时和string无区别,只需确保val为null时正确赋值。 - 通用可空值类型处理:通过
Nullable.GetUnderlyingType判断是否为可空值类型,统一解析逻辑。 - 新增通用解析方法:
TryParseValue复用解析逻辑,支持更多基础值类型(如long、bool等),避免重复代码。 - 优化实例创建:使用
new T()并添加where T : new()约束,比Activator.CreateInstance<T>()更高效且有编译时检查。 - 忽略大小写的Key匹配:使用
StringComparison.OrdinalIgnoreCase避免因大小写不一致导致的匹配失败(可根据需求调整)。
针对你的疑问补充:
为什么 int? 能用 typeof 判断而 string? 不行?
int是值类型,int?本质是Nullable<int>结构体,属于不同的运行时类型。string是引用类型,string?只是编译阶段的注解,用于提示编译器进行空值检查,运行时没有对应的新类型,因此typeof(string?)和typeof(string)返回同一个Type对象。
内容的提问来源于stack exchange,提问作者Dorian
相关产品推荐
相关产品推荐

