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
相关产品推荐
相关产品推荐

