我写了一个漂亮的方法来深度复制任何对象。它通过递归调用MemberwiseClone()
实例内的任何引用类型字段来实现。此方法适用于我想使用的任何对象,包括层次关系对象。该方法还显示了过去访问的字典,因此避免了不必要的重复工作。
但是,我遇到的问题是,此方法仅在需要克隆时对象不是绑定到 WPF/MVVM 的数据时才有效。当数据绑定和方法被调用时,我遇到堆栈溢出异常,因为(我假设)INotifyPropertyChanged.PropertyChanged
事件和 WPF 框架之间建立了链接。然后递归调用尝试复制整个对象世界,包括AppDomain
和低级指针对象,这些对象似乎是链接的并继续几乎无限(无论如何,超过 VS2012 可以处理的。)
我怀疑我是否需要深度复制一个可以追溯到开头的对象图AppDomain
......是否有一种聪明的方法可以让我的复制方法在达到某个边界时“停止”?我还考虑过在数据绑定之前简单地复制对象,但我不确定这是一个可行的选择,而且相当愚蠢。我只想要一个简单的深拷贝解决方案,它适用于不可序列化的类型,但也可以通过INotifyPropertyChanged
.
该方法的实现:
private static object Clone(object instance, IDictionary<object, object> visitGraph)
{
var instanceType = instance.GetType();
Debug.WriteLine(instanceType.Name);
object clonedInstance = null;
if (visitGraph.ContainsKey(instance))
{
clonedInstance = visitGraph[instance];
}
else
{
const BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Instance;
var memberwiseCloneMethod =
instanceType.GetMethods(flags).Single(x => x.Name == "MemberwiseClone" &&
!x.GetParameters().Any());
clonedInstance = memberwiseCloneMethod.Invoke(instance, null);
visitGraph.Add(instance, clonedInstance);
var allReferenceTypeProperties = clonedInstance.GetType().GetAllFields()
.Where(
x =>
!x.FieldType.IsValueType
&& x.FieldType != typeof (string));
foreach (var field in allReferenceTypeProperties)
{
var existingFieldValue = field.GetValue(instance);
if (existingFieldValue != null)
{
var clonedFieldValue = Clone(existingFieldValue, visitGraph);
field.SetValue(clonedInstance, clonedFieldValue);
}
}
}
return clonedInstance;
}
public static IEnumerable<FieldInfo> GetAllFields(this Type type)
{
const BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic |
BindingFlags.Instance;
var fields = type.GetFields(flags);
foreach (var field in fields)
{
yield return field;
}
if (type.BaseType != null)
{
foreach (var field in GetAllFields(type.BaseType))
{
yield return field;
}
}
}
public static object Copy(this object instance)
{
if (instance == null) throw new ArgumentNullException("instance");
var visitGraph = new Dictionary<object, object>();
var clonedInstance = Clone(instance, visitGraph);
return clonedInstance;
}