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

如何将MyParameter深拷贝至已有实例并保留PropertyChanged事件处理器

问题描述

我需要将<MyParameter>类型的par0深拷贝到已有的<MyParameter>实例par1中,其中par1的部分子元素已注册PropertyChanged事件处理器,且这些处理器不允许被覆盖。

我测试过Automapper、AnyClone等工具及相关代码片段,但都需要显式列出所有字段,而这正是我想要避免的(BinaryFormatter已被弃用,Automapper的ForAllOtherMembers已被移除)。

以下是简化后的类结构示例。我的ShallowCopyFieldsTest()方法仅能实现浅拷贝(可参考MyButton()方法),我希望能使用类似DeepCopyFields(par0, par1)的通用方法。恳请帮助,谢谢!


/Models/Parameter.cs

public partial class MyDouble : ObservableObject
{
    [ObservableProperty]
    private double value;
    [ObservableProperty]
    private double min;
    [ObservableProperty]
    private string info;
}

public class MyGeometry
{
    public MyDouble Diameter { get; set; } = new();
    public MyDouble Lenght { get; set; } = new();

    public MyGeometry()
    {
        Diameter.Value = 10.0;
        Diameter.Min = 0.001;
        Diameter.Info = "Diameter";

        Lenght.Value = 20.0;
        Lenght.Min = 0.001;
        Lenght.Info = "Length";
    }
}

public class MyPhysics
{
    public MyDouble F0 { get; set; } = new();
    public MyDouble F1 { get; set; } = new();

    public MyPhysics()
    {
        F0.Value = 1000.0;
        F0.Min = 0.001;
        F0.Info = "Force0";

        F1.Value = 2000.0;
        F1.Min = 0.001;
        F1.Info = "Force1";
    }
}

public class MyParameter 
{
    public MyDouble XYZ { get; set; } = new();

    public MyGeometry Geometry0 { get; set; } = new();
    public MyGeometry Geometry1 { get; set; } = new();
    public MyPhysics Physics { get; set; } = new();

    public MyParameter()
    {
        XYZ.Value = 1000.0;
        XYZ.Min = 0.001;
        XYZ.Info = "xyz";
    }
}

/ViewModels/MainViewModel.cs

[ObservableProperty]
public MyParameter par0 = new();
[ObservableProperty]
public MyParameter par1 = new();

[RelayCommand]
public void MyButton()
{
    ShallowCopyFieldsTest(par0, par1); 
    ShallowCopyFieldsTest(par0.Geometry0, par1.Geometry0);
    ShallowCopyFieldsTest(par0.Geometry1, par1.Geometry1);
    ShallowCopyFieldsTest(par0.Physics, par1.Physics);
    // ShallowCopyFieldsTest(par0.some_deeper_structure...Physics, par1.some_deeper_structure...Physics);

    // TODO: Better solution -->  DeepCopyFields(par0, par1);
}

/Helpers/Helper.cs

public static void ShallowCopyFieldsTest(object _source, object _destin)
{
    foreach (var prop in _source.GetType().GetProperties().ToList())
    {
        try
        { 
            FieldInfo fi;
            fi = typeof(MyDouble).GetField("value", BindingFlags.NonPublic | BindingFlags.Instance);
            fi.SetValue(prop.GetValue(_destin), fi.GetValue(prop.GetValue(_source)));

            fi = typeof(MyDouble).GetField("min", BindingFlags.NonPublic | BindingFlags.Instance);
            fi.SetValue(prop.GetValue(_destin), fi.GetValue(prop.GetValue(_source)));

            fi = typeof(MyDouble).GetField("info", BindingFlags.NonPublic | BindingFlags.Instance);
            fi.SetValue(prop.GetValue(_destin), fi.GetValue(prop.GetValue(_source)));
        }
        catch { }
    }
}

解决方案

可以实现一个递归的DeepCopyFields通用方法,核心逻辑是:

  • 遍历源对象属性,区分值类型/引用类型分别处理
  • 对MyDouble这类带事件的类型,跳过事件字段直接复制数据字段,保留已注册的处理器
  • 对自定义引用类型(如MyGeometry、MyPhysics)递归完成深层复制

以下是实现代码:

using System.Reflection;
using CommunityToolkit.Mvvm.ComponentModel;

public static class DeepCopyHelper
{
    // 定义需要递归处理的自定义引用类型集合
    private static readonly HashSet<Type> _recursiveTypes = new()
    {
        typeof(MyParameter),
        typeof(MyGeometry),
        typeof(MyPhysics)
    };

    public static void DeepCopyFields(object source, object destination)
    {
        if (source == null || destination == null)
            throw new ArgumentNullException(nameof(source), "源对象或目标对象不能为空");
        if (source.GetType() != destination.GetType())
            throw new ArgumentException("源对象和目标对象类型必须一致");

        var type = source.GetType();

        // 处理MyDouble类型:复制数据字段,跳过事件
        if (type == typeof(MyDouble))
        {
            CopyMyDoubleFields((MyDouble)source, (MyDouble)destination);
            return;
        }

        // 处理需要递归的自定义引用类型
        if (_recursiveTypes.Contains(type))
        {
            foreach (var prop in type.GetProperties(BindingFlags.Public | BindingFlags.Instance))
            {
                var sourceVal = prop.GetValue(source);
                var destVal = prop.GetValue(destination);

                if (sourceVal == null || destVal == null)
                    continue;

                // 递归处理子属性
                DeepCopyFields(sourceVal, destVal);
            }
            return;
        }

        // 处理值类型和字符串
        if (type.IsValueType || type == typeof(string))
        {
            prop.SetValue(destination, sourceVal);
        }
    }

    private static void CopyMyDoubleFields(MyDouble source, MyDouble destination)
    {
        var fields = typeof(MyDouble).GetFields(BindingFlags.NonPublic | BindingFlags.Instance);
        foreach (var field in fields)
        {
            // 跳过PropertyChanged事件字段,避免覆盖已注册的处理器
            if (field.FieldType == typeof(PropertyChangedEventHandler))
                continue;

            var value = field.GetValue(source);
            field.SetValue(destination, value);
        }
    }
}

使用方式

在MyButton方法中直接调用即可完成全层级深拷贝:

[RelayCommand]
public void MyButton()
{
    DeepCopyHelper.DeepCopyFields(par0, par1);
}

说明

  • 无需显式列出所有字段,方法会自动遍历处理
  • 严格保留par1中已注册的PropertyChanged事件处理器
  • _recursiveTypes集合可根据业务需求扩展需要递归处理的自定义类型

内容的提问来源于stack exchange,提问作者Zulu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 21:40:35