C# DataTable转自定义对象遇类型转换异常,如何实现健壮处理?
问题:DataTable转自定义对象时的类型转换异常处理
当Excel上传生成的DataTable中存在非法值(如Year列输入2023g这类无法识别为数值的内容),将其转换为自定义对象ExcelTemplateRow列表时,会抛出类型转换异常:
Object of type 'System.String' cannot be converted to type 'System.Nullable`1[System.Double]'
现有转换方法代码:
public static List<T> ConvertToList<T>(DataTable dt) { var columnNames = dt.Columns.Cast<DataColumn>().Select(c => c.ColumnName.ToLower()).ToList(); var trimmedColumnNames = new List<string>(); foreach (var columnName in columnNames) { trimmedColumnNames.Add(columnName.Trim().ToLower()); } var properties = typeof(T).GetProperties(); return dt.AsEnumerable().Select(row => { var objT = Activator.CreateInstance<T>(); foreach (var property in properties) { if (trimmedColumnNames.Contains(property.Name.Trim().ToLower())) { try { if(row[property.Name] != DBNull.Value) { property.SetValue(objT, row[property.Name]); } else { property.SetValue(objT, null); } } catch (Exception ex) { throw ex; } } } return objT; }).ToList(); }
自定义对象定义:
public class ExcelTemplateRow { public string? Country {get; set;} public double? Year {get; set;} // 其他属性... }
优化方案
核心是针对目标属性的类型做显式类型解析与转换,而非直接赋值,同时处理转换失败的场景,保证方法健壮性:
优化后的转换方法
public static List<T> ConvertToList<T>(DataTable dt) where T : new() { // 预处理列名(统一小写+去空格) var trimmedColumnNames = dt.Columns .Cast<DataColumn>() .Select(c => c.ColumnName.Trim().ToLower()) .ToList(); var properties = typeof(T).GetProperties(); return dt.AsEnumerable().Select(row => { var objT = new T(); foreach (var property in properties) { var propertyNameLower = property.Name.Trim().ToLower(); if (!trimmedColumnNames.Contains(propertyNameLower)) continue; var cellValue = row[property.Name]; if (cellValue == DBNull.Value) { property.SetValue(objT, null); continue; } // 获取目标类型(处理Nullable类型,取其底层值类型) var targetType = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType; try { object convertedValue = null; // 根据目标类型做针对性转换 if (targetType == typeof(double)) { if (double.TryParse(cellValue.ToString(), out var doubleVal)) convertedValue = doubleVal; } else if (targetType == typeof(int)) { if (int.TryParse(cellValue.ToString(), out var intVal)) convertedValue = intVal; } else if (targetType == typeof(string)) { convertedValue = cellValue.ToString()?.Trim(); } // 可扩展更多类型(如DateTime、bool等) else { // 默认尝试类型转换(适用于可直接转换的场景) convertedValue = Convert.ChangeType(cellValue, targetType); } property.SetValue(objT, convertedValue); } catch { // 转换失败时设为null(或可根据需求记录错误日志) property.SetValue(objT, null); } } return objT; }).ToList(); }
关键优化点说明
- 处理Nullable类型:通过
Nullable.GetUnderlyingType获取Nullable属性的底层值类型,避免转换时的类型不匹配问题 - 针对性类型解析:对数值类型(如double、int)使用
TryParse方法,安全处理非法输入,转换失败时自动设为null - 简化列名预处理:用LINQ简化列名的去空格+小写操作,代码更简洁
- 异常处理优化:捕获转换异常时直接设为null(也可根据需求添加错误日志记录,方便排查问题)
- 约束泛型类型:添加
where T : new()约束,替代Activator.CreateInstance<T>(),代码更简洁且性能更优
为什么改object属性没用?
将Year改为object?类型只是规避了编译时的类型检查,但DataTable中非法的string值依然会被直接赋值,既失去了强类型的优势,也没有处理非法值的逻辑,无法从根本上解决问题。
内容的提问来源于stack exchange,提问作者The Inquisitive Coder
相关产品推荐
相关产品推荐

