【发布时间】:2014-02-06 00:21:18
【问题描述】:
我必须实现通用扩展 deepclone 方法,该方法可以与任何引用类型实例一起使用以获取其深层副本。我将其实现为以下
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;
}
}
效果很好。但是,如果要克隆的对象及其引用类型字段具有相互引用,则此代码会导致无限循环。 例如
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; }
}
有谁知道如何实现这个任务?它应该按字面意思实施,不允许有任何变化(这是一个实习)。非常感谢任何提示!
【问题讨论】:
-
看不到通用深度克隆方法是有原因的。为了正确完成它们实际上需要根据所讨论的特定类型来实现。鉴于您应该非常很少需要实际使用此类实现,这通常不是问题。您需要这样的东西实际上表明可能有问题。
-
我明白了。但这是一个实习,所以我必须以某种方式去做。我不知道,也许为了解决这个问题,我应该为已经克隆的对象添加一些存储空间,以免再次克隆它们。但这会为所有对象链提供深层副本吗?我在这里真的很绝望
-
您必须保留每个克隆对象的字典(通过引用或hash 键入)及其克隆的引用。在克隆新对象之前,请检查它是否已被克隆。
标签: c# recursion extension-methods deep-copy