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

C#反射:如何将数据转换为属性类型并设置属性值?

反射实现SqlDataReader到实体类的属性赋值(含类型转换处理)

刚接触反射的时候确实容易被类型转换的问题搞懵,我当初第一次写类似代码时,也踩过直接赋值导致类型不匹配、DBNull报错的坑。咱们先看看你这段代码的核心问题,再一步步优化:

首先,先把你的代码片段贴出来方便分析:

protected override object Load(SqlDataReader dr) { 
    object item = Activator.CreateInstance(klass); 
    foreach (var p in klass.GetProperties()) { 
        MethodInfo pSet = p.GetSetMethod(); 
        Type pType= p.PropertyType; 
        object setParam = dr[p.Name]; 
        object[] paramArray = (object[])Array.CreateInstance(pType, 1); 
        paramArray[0] = setParam; 
        // 这里应该是调用pSet.Invoke(item, paramArray)?
    }
}

核心问题分析

这段代码的主要隐患在于:

  • 没有处理DBNull.Value:如果数据库字段为NULL,dr[p.Name]返回的是DBNull.Value,直接赋值给值类型属性会抛出异常。
  • 没有做类型转换:比如数据库里的varchar对应实体的int,或者datetime对应实体的DateTime?,直接赋值会触发类型不匹配错误。
  • 赋值方式可以更简洁:不用手动获取SetMethod,直接用PropertyInfo.SetValue更直观。

优化后的代码实现

下面是修复了类型转换和DBNull处理的版本:

protected override object Load(SqlDataReader dr)
{
    object item = Activator.CreateInstance(klass);
    foreach (var p in klass.GetProperties())
    {
        // 跳过没有Setter的属性
        if (!p.CanWrite) continue;

        Type propertyType = p.PropertyType;
        object dbValue = dr[p.Name];

        // 处理DBNull的情况
        if (dbValue == DBNull.Value)
        {
            // 如果是值类型且不是可空类型,设为默认值;否则设为null
            if (propertyType.IsValueType && Nullable.GetUnderlyingType(propertyType) == null)
            {
                p.SetValue(item, Activator.CreateInstance(propertyType));
            }
            else
            {
                p.SetValue(item, null);
            }
            continue;
        }

        // 处理类型转换:把数据库值转换成属性对应的类型
        object convertedValue;
        try
        {
            // 兼容可空类型,获取其底层值类型
            Type targetType = Nullable.GetUnderlyingType(propertyType) ?? propertyType;
            convertedValue = Convert.ChangeType(dbValue, targetType);
        }
        catch (InvalidCastException ex)
        {
            // 可在这里添加自定义转换逻辑,比如字符串转枚举、特殊日期格式等
            throw new InvalidOperationException($"无法将字段{p.Name}的值{dbValue}转换为类型{propertyType.Name}", ex);
        }

        // 给属性赋值
        p.SetValue(item, convertedValue);
    }
    return item;
}

关键细节说明

  • DBNull处理:区分值类型、可空类型和引用类型,分别设置默认值或null,避免空引用或类型异常。
  • 可空类型兼容:用Nullable.GetUnderlyingType提取可空类型的底层值类型(比如int?的底层是int),确保转换逻辑正确。
  • 类型转换容错:用try-catch包裹转换过程,方便定位特定字段的转换问题,也能在catch块里扩展自定义转换规则(比如把字符串转成枚举类型)。
  • 简化赋值逻辑:直接使用p.SetValue(item, convertedValue)替代手动获取SetMethod并调用Invoke,代码更简洁易读。

如果还有特殊类型(比如自定义结构体、枚举)的转换需求,还可以在转换部分加额外的判断逻辑,比如判断propertyType.IsEnum时,用Enum.Parse来完成转换。

内容的提问来源于stack exchange,提问作者João Santos

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 11:19:40