1

我写了一个漂亮的方法来深度复制任何对象。它通过递归调用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;
}
4

1 回答 1

0

我正在用快捷解决方案回答我自己的问题。我更喜欢永久的解决方案,但我没有奢侈。

我将深度克隆方法修改为浅(而不是深)复制 EventHandler 对象。这些对象不再是真正的深层克隆,但这对我的应用程序来说不是问题。不过,我想要一个适用于所有情况的解决方案。

于 2013-02-12T17:36:40.910 回答