如何通过反射实现泛型DataStructure类间的属性转换与复制?
如何通过反射实现泛型DataStructure类间的属性转换与复制?
这种手动写映射的痛苦我太懂了——结构稍微一变,所有复制代码都得跟着改,完全是重复劳动。用反射做通用的类型转换复制绝对是最优解,还能完美支持派生类。下面我给你一步步实现这个逻辑,直接就能用到你的代码里:
1. 先定义灵活的转换委托
首先我们需要一个通用的委托来传递自定义的 value 转换逻辑,这样你可以根据不同的类型对(比如string→double、int→bool)写专属的转换规则,还能处理异常、特殊值映射等场景:
// 通用值转换委托,让调用者灵活定义转换逻辑 public delegate TTarget ValueConverter<TSource, TTarget>(TSource sourceValue);
2. 核心递归复制方法
这个方法是整个逻辑的核心,会自动递归处理所有层级的属性、嵌套Category、集合等结构,不需要手动写每个属性的映射:
// 递归处理对象属性的复制与类型转换 private static void CopyProperties<TSource, TTarget>(object sourceObj, object targetObj, ValueConverter<TSource, TTarget> converter) { if (sourceObj == null || targetObj == null) return; var sourceType = sourceObj.GetType(); var targetType = targetObj.GetType(); // 获取源对象的所有公共实例属性 var sourceProperties = sourceType.GetProperties(BindingFlags.Instance | BindingFlags.Public); foreach (var sourceProp in sourceProperties) { // 跳过无读取权限的属性 if (!sourceProp.CanRead) continue; var sourceValue = sourceProp.GetValue(sourceObj); // 跳过null值(可根据业务需求调整是否保留此判断) if (sourceValue == null) continue; // 找到目标对象中同名的公共可写属性 var targetProp = targetType.GetProperty(sourceProp.Name, BindingFlags.Instance | BindingFlags.Public); if (targetProp == null || !targetProp.CanWrite) { // 可选:抛出异常或打日志,提示源和目标结构不匹配 continue; } var targetValue = targetProp.GetValue(targetObj); // 情况1:处理DataProperty<TSource> → DataProperty<TTarget>的类型转换 if (IsDataProperty(sourceProp.PropertyType, typeof(TSource)) && IsDataProperty(targetProp.PropertyType, typeof(TTarget))) { var sourceDataProp = sourceValue as DataProperty<TSource>; var targetDataProp = targetValue as DataProperty<TTarget>; if (sourceDataProp != null && targetDataProp != null) { targetDataProp.Value = converter(sourceDataProp.Value); } continue; } // 情况2:处理List<Category>这类集合(比如Forces) if (sourceValue is IEnumerable<ICategory> sourceCategoryList && targetValue is IList targetList) { targetList.Clear(); // 清空目标集合(可根据需求改为追加) var targetItemType = targetList.GetType().GetGenericArguments()[0]; foreach (var sourceItem in sourceCategoryList) { // 创建目标集合的元素实例 var targetItem = Activator.CreateInstance(targetItemType); // 递归复制元素内部的所有属性 CopyProperties(sourceItem, targetItem, converter); targetList.Add(targetItem); } continue; } // 情况3:处理Category及其子类(比如EnvironmentData、LightData这类嵌套类) if (sourceValue is Category sourceCategory && targetValue is Category targetCategory) { CopyProperties(sourceCategory, targetCategory, converter); continue; } // 情况4:其他可直接赋值的类型(比如值类型、普通引用类型) if (targetProp.PropertyType.IsAssignableFrom(sourceProp.PropertyType)) { targetProp.SetValue(targetObj, sourceValue); } } } // 辅助方法:判断属性是否为指定泛型参数的DataProperty private static bool IsDataProperty(Type propertyType, Type genericArgType) { return propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(DataProperty<>) && propertyType.GetGenericArguments()[0] == genericArgType; }
3. 对外暴露的通用转换方法
我们写两个重载方法,一个支持直接使用DataStructure<TTarget>,另一个支持目标是DataStructure的派生类:
// 核心对外方法:支持目标为DataStructure的派生类 public static void ConvertAndCopyData<TSource, TTarget, TDerivedTarget>(DataStructure<TSource> source, TDerivedTarget target, ValueConverter<TSource, TTarget> converter) where TDerivedTarget : DataStructure<TTarget> { if (source == null || target == null || converter == null) throw new ArgumentNullException("源对象、目标对象或转换器不能为空"); // 触发递归复制所有层级的属性 CopyProperties(source, target, converter); } // 重载方法:简化直接使用DataStructure<TTarget>的场景 public static void ConvertAndCopyData<TSource, TTarget>(DataStructure<TSource> source, DataStructure<TTarget> target, ValueConverter<TSource, TTarget> converter) { ConvertAndCopyData(source, target, converter); }
4. 实际使用示例
在你的Main方法里直接调用即可,还能支持派生类场景:
static void Main(string[] args) { DataStructure<string> dataStructureStr = new(); FillData(dataStructureStr); // 普通DataStructure<double>的转换场景 DataStructure<double> dataStructureDbl = new(); ConvertAndCopyData(dataStructureStr, dataStructureDbl, s => { // 自定义string转double的逻辑,可添加异常处理 if (double.TryParse(s, out double result)) return result; throw new FormatException($"无法将字符串「{s}」转换为double类型"); }); // 支持派生类的转换场景 DerivedDataStructure<double> derivedDbl = new(); ConvertAndCopyData(dataStructureStr, derivedDbl, s => double.Parse(s)); } // 示例派生类(演示支持自定义扩展的DataStructure) public class DerivedDataStructure<T> : DataStructure<T> { public ExtraData Extra { get; init; } = new(); public class ExtraData : Category { public DataProperty<T> CustomValue { get; init; } = new(); } }
关键细节说明
- 全层级递归处理:不管是多少层嵌套的
Category(比如Environment→Light→LightPosition),都会自动递归复制内部属性,完全不用手动维护层级映射。 - 集合结构同步:对
List<Category>这类集合(比如Forces),会自动遍历源集合,创建目标集合的元素实例并复制属性,保证集合的元素数量和内部结构完全同步。 - 派生类完美支持:反射会自动获取目标对象的实际运行类型(比如
DerivedDataStructure),所以派生类的额外属性只要源对象有对应结构,就会自动转换复制。 - 转换逻辑灵活:通过
ValueConverter委托,你可以完全自定义不同类型对的转换规则,比如处理空值、异常、特殊业务值映射等。
备注:内容来源于stack exchange,提问作者aluc_2513
相关产品推荐
相关产品推荐

