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

如何通过反射实现泛型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();
    }
}

关键细节说明

  1. 全层级递归处理:不管是多少层嵌套的Category(比如Environment→Light→LightPosition),都会自动递归复制内部属性,完全不用手动维护层级映射。
  2. 集合结构同步:对List<Category>这类集合(比如Forces),会自动遍历源集合,创建目标集合的元素实例并复制属性,保证集合的元素数量和内部结构完全同步。
  3. 派生类完美支持:反射会自动获取目标对象的实际运行类型(比如DerivedDataStructure),所以派生类的额外属性只要源对象有对应结构,就会自动转换复制。
  4. 转换逻辑灵活:通过ValueConverter委托,你可以完全自定义不同类型对的转换规则,比如处理空值、异常、特殊业务值映射等。

备注:内容来源于stack exchange,提问作者aluc_2513

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.20 12:54:35