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

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();
}

关键优化点说明

  1. 处理Nullable类型:通过Nullable.GetUnderlyingType获取Nullable属性的底层值类型,避免转换时的类型不匹配问题
  2. 针对性类型解析:对数值类型(如double、int)使用TryParse方法,安全处理非法输入,转换失败时自动设为null
  3. 简化列名预处理:用LINQ简化列名的去空格+小写操作,代码更简洁
  4. 异常处理优化:捕获转换异常时直接设为null(也可根据需求添加错误日志记录,方便排查问题)
  5. 约束泛型类型:添加where T : new()约束,替代Activator.CreateInstance<T>(),代码更简洁且性能更优

为什么改object属性没用?

将Year改为object?类型只是规避了编译时的类型检查,但DataTable中非法的string值依然会被直接赋值,既失去了强类型的优势,也没有处理非法值的逻辑,无法从根本上解决问题。

内容的提问来源于stack exchange,提问作者The Inquisitive Coder

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 14:20:40