C#如何将字符串转换为数据对象及字段引用实现动态赋值
C#动态映射配置赋值实现方案
针对字符串形式的类名、属性名动态赋值需求,无需手写多层switch分支,直接使用反射+元数据缓存方案即可实现,兼顾扩展性与性能。
核心实现代码
首先实现通用的动态赋值工具类,加入缓存避免重复反射带来的性能损耗:
using System.Reflection; public static class EntityMapper { // 缓存:类名 -> 对应类型元数据 private static readonly Dictionary<string, Type> _typeCache = new(); // 缓存:类名.属性名 -> 对应属性元数据 private static readonly Dictionary<string, PropertyInfo> _propertyCache = new(); /// <summary> /// 动态创建实体实例并给指定属性赋值 /// </summary> /// <param name="className">数据库读取的目标类名</param> /// <param name="propertyName">数据库读取的目标属性名</param> /// <param name="value">要赋值的字段值</param> /// <returns>赋值完成的实体实例</returns> /// <exception cref="ArgumentException">找不到对应类或可写属性时抛出</exception> public static object AssignProperty(string className, string propertyName, string value) { // 查找目标类型,优先读缓存 if (!_typeCache.TryGetValue(className, out Type targetType)) { // 扫描当前程序集的所有类匹配类名,若实体在其他程序集可加入对应程序集扫描 targetType = Assembly.GetExecutingAssembly() .GetTypes() .FirstOrDefault(t => t.IsClass && t.Name == className); if (targetType == null) throw new ArgumentException($"未找到匹配的实体类:{className}"); _typeCache.TryAdd(className, targetType); } // 创建实体实例,若需复用已有实例可跳过该步,直接传入实例作为参数 object instance = Activator.CreateInstance(targetType); // 查找目标属性,优先读缓存 string propCacheKey = $"{className}.{propertyName}"; if (!_propertyCache.TryGetValue(propCacheKey, out PropertyInfo targetProp)) { targetProp = targetType.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance); if (targetProp == null || !targetProp.CanWrite) throw new ArgumentException($"类{className}中不存在可写属性:{propertyName}"); _propertyCache.TryAdd(propCacheKey, targetProp); } // 执行赋值,若属性为非字符串类型可在此处增加通用类型转换逻辑 targetProp.SetValue(instance, value); return instance; } // 保留原有的字段读取工具方法 public static string TryGetString(string[] transLine, int position) { return position > transLine.Length - 1 ? string.Empty : transLine[position]; } }
业务场景调用方式
对接分隔符文件读取、数据库配置加载的逻辑时,无需写任何case或switch判断,直接读取配置调用通用方法即可:
// 以下四个值均从数据库配置中读取 string targetEntityName = "TransLevel2"; string targetFieldName = "CD_FIELD_1"; string lineIdentifier = "SAC"; int fieldPosition = 1; // 逐行读取分隔符文件 foreach (string[] transLine in GetFileLines()) { string currentLineFlag = GetLineFlag(transLine); // 按业务逻辑获取当前行标识 if (currentLineFlag != lineIdentifier) continue; // 读取指定位置的字段值 string fieldValue = EntityMapper.TryGetString(transLine, fieldPosition); // 动态完成对象创建与赋值,等价于手写 TransLevel2.CD_FIELD_1 = fieldValue object entity = EntityMapper.AssignProperty(targetEntityName, targetFieldName, fieldValue); // 如需强类型处理,可按类型转换后使用 if (entity is TransLevel2 level2Entity) { // 执行TransLevel2对应的后续业务逻辑 } }
方案优势
- 无硬编码分支:后续新增实体类型、新增字段映射规则,仅需在数据库中新增配置项,无需修改业务代码、无需重新编译发布
- 性能损耗极低:类型、属性元数据全局缓存,仅首次加载对应配置时执行反射查找,后续赋值性能与硬编码写法几乎一致
- 扩展成本低:如果后续需要支持非字符串类型的属性(比如int、DateTime、枚举),仅需在赋值逻辑中增加一处通用类型转换代码,无需逐个分支修改
注意事项
- 若项目中存在重名实体类,建议数据库配置中存储带命名空间的完整类名(如
YourProject.Entities.TransLevel2),直接通过Type.GetType(fullClassName)获取类型,避免匹配错误 - 若业务场景中不需要每次新建对象(比如复用已初始化的实体实例),可以去掉方法内
Activator.CreateInstance的逻辑,直接将已有实例作为参数传入方法完成赋值 - 若实体存在非string类型的属性,可在赋值前增加通用类型转换逻辑,自动将读取到的字符串值转换为属性对应类型,适配更多业务场景
内容的提问来源于stack exchange,提问作者VBartilucci
相关产品推荐
相关产品推荐

