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

