对象深度克隆实现

我必须实现generics扩展deepclone方法,该方法可以与任何引用类型实例一起使用以获取其深层副本。 我实现如下

static class ClassCopy { static public T DeepClone (this T instance) { if (instance == null) return null; var type = instance.GetType(); T copy; var flags = BindingFlags.FlattenHierarchy | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; var fields = type.GetFields(flags); // If type is serializable - create instance copy using BinaryFormatter if (type.IsSerializable) { using (var stream = new MemoryStream()) { var formatter = new BinaryFormatter(); formatter.Serialize(stream, instance); stream.Position = 0; copy = (T) formatter.Deserialize(stream); } // Copy all fiels which are not marked as serializable foreach (var field in fields) { if (!field.IsNotSerialized) continue; var value = field.GetValue(instance); //Recursion!!! //for each embedded object also create deep copy value = value != null ? value.DeepClone() : value; field.SetValue(copy, value); } } else { // If type is not serializable - create instance copy using Activator //(if there is default constructor) // or FormatterServices ( if there is no constractor) copy = CreateInstance(type); foreach (var field in fields) { var value = field.GetValue(instance); //Recursion!!! value = value != null ? value.DeepClone() : value; field.SetValue(copy, value); } } //Copy all properties //In order to copy all backing fields for auto-implemented properties var properties = type.GetProperties(flags|BindingFlags.SetProperty); foreach (var property in properties) { if (property.CanWrite) { var value = property.GetValue(instance); //Recursion!!! value = value != null ? value.DeepClone() : null; property.SetValue(copy, value); } } return copy; } private static T CreateInstance(Type t) where T: class { T instance; var constructor = t.GetConstructor(Type.EmptyTypes); if (constructor != null) { instance = Activator.CreateInstance(t) as T; return instance; } instance = FormatterServices.GetUninitializedObject(t) as T; return instance; } } 

它运作良好。 但是如果要克隆的对象及其引用类型字段具有相互引用,则此代码会导致无限循环。 例如

 private static void Main(string[] args) { var parent = new Parent(); parent.Child = new Child(); parent.Child.Parent = parent; //Infinite Loop!!! var parent1 = parent.DeepClone(); } class Parent { public Child Child { get; set; } } class Child { public Parent Parent { get; set; } } 

有谁知道如何实现这个任务? 它应该按字面意思实现,不允许任何变化(这是实习)。 非常感谢任何提示!

您可以做的是传递映射到其克隆的项目Dictionary 。 现在该方法将如下所示:

static private T DeepClone (this T instance, IDictionary originalToAlreadyCloned) where T: class

现在你做的第一件事就是if (instance == null) return null; ,检查originalToAlreadyCloned是否存在instance ,如果是,则返回它。

为了填充它,之后

  1. copy = (T) formatter.Deserialize(stream);
  2. copy = CreateInstance(type);

调用originalToAlreadyCloned.Add(instance, copy);

最后,提供一个新的顶级方法:

static private T DeepClone (this T instance) where T: class只调用DeepClone(instance, new Dictionary());

顺便说一下, value = value != null && value.GetType().IsClass? value.DeepClone() : null; value = value != null && value.GetType().IsClass? value.DeepClone() : null; 似乎错了。 你所说的是,如果value不是类,则将其设置为null。 但是如果它不是一个类,通常不能将它设置为null。 我不确定为什么你不只是克隆这些项目,以及为什么DeepClone仅限于类。

深度克隆对象的一个​​老技巧是对它们进行序列化和反序列化,从而创建新实例。

 public T deepClone(T toClone) where T : class { string tmp = JsonConvert.SerializeObject(toClone); return JsonConvert.DeserializeObject(tmp); } 

我与Newtonsoft.Json广泛的合作,它有一个内置的解决方案来解决你的问题。 默认情况下,它会检测对象是否已经序列化并引发exception。 但是,您可以将其配置为序列化对象的引用以获取循环引用。 它不是串行化对象,而是序列化对该对象的引用,并保证对对象的每个引用只被序列化一次。

此外,默认是仅序列化公共字段/属性。 还有一个用于序列化私有字段的附加设置。

 public T deepClone(T toClone) where T : class { JsonSerializerSettings settings = new JsonSerializerSettings(); settings.PreserveReferencesHandling = PreserveReferencesHandling.Objects; DefaultContractResolver dcr = new DefaultContractResolver(); dcr.DefaultMembersSearchFlags |= System.Reflection.BindingFlags.NonPublic; settings.ContractResolver = dcr; string tmp = JsonConvert.SerializeObject(toClone, settings); return JsonConvert.DeserializeObject(tmp); } 

所以你可以“欺骗”并使用这样的代码或复制它如何工作来实现保留引用的克隆。 你给父母/孩子的例子只是单向克隆很难。 1到很多是另一个。