对象深层克隆实现 [英] Object deep clone implementation
问题描述
我必须实现通用扩展deepclone方法,该方法可与任何引用类型实例一起使用以获取其深层副本。我将其实现为以下
I have to implement generic extention deepclone method which can be used with any reference type instance to get its deep copy. I implement it as the following
static class ClassCopy
{
static public T DeepClone<T> (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<T>(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<T>(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;
}
}
效果很好。但是,如果要克隆的对象及其引用类型字段具有相互引用,则此代码将导致无限循环。
例如
It works well. But if object to be cloned and its reference type fields have mutual references this code leads to infinite loops. e.g.
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; }
}
有人知道如何实现此任务吗?应该从字面上实现它,并且不允许任何变化(这是一种实践)。非常感谢您提供的任何提示!
Does anyone has any idea how to implement this task? It should be implemented literally and no variations are allowed (it's a practicum). Thanks a lot for any tips!
推荐答案
您可以做的就是绕过 Dictionary
映射到其克隆的项目。现在该方法将如下所示:
What you can do is pass around a Dictionary
of items mapped to their clones. Now the method will look like this:
静态私有T DeepClone< T> (此T实例,IDictionary< object,object> originalToAlreadyCloned),其中T:class
现在,您在<$ c $之后要做的第一件事c>如果(instance == null)返回null; ,检查 instance
是否存在于 originalToAlreadyCloned $中c $ c>,如果是,则将其返回。
Now the first thing you do after if (instance == null) return null;
, is check if instance
is present in originalToAlreadyCloned
and if so, return it.
为了填充它,在
- $ b之后$ b
-
copy =(T)formatter.Deserialize(stream);
-
copy = CreateInstance< ; T>(类型);
copy = (T) formatter.Deserialize(stream);
copy = CreateInstance<T>(type);
调用 originalToAlreadyCloned.Add(instance,复制);
最后,提供一种新的顶级方法:
Finally, provide a new top-level method:
静态私有T DeepClone< T> (此T实例),其中T:类
只需调用 DeepClone(instance,new Dictionary< object,object>());
顺便说一下, value = value!= null& value.GetType()。IsClass? value.DeepClone():null;
似乎是错误的。您的意思是,如果 value
不是类,请将其设置为null。但是,如果不是课程,则通常不能将其设置为null。我不确定您为什么不也克隆这些项目,以及为什么 DeepClone
仅限于类。
By the way, value = value != null && value.GetType().IsClass? value.DeepClone() : null;
seems wrong. What you're saying is, if value
is not a class, set it to null. But if it's not a class, you can't generally set it to null. I'm not sure why you don't just clone these items too, and why DeepClone
is restricted to only classes.
这篇关于对象深层克隆实现的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!