深度复制WPF数据绑定对象时

时间:2013-02-04 05:55:12

标签: wpf copy inotifypropertychanged

我写了一个漂亮的方法来深度复制任何对象。它通过在实例内的任何引用类型字段上递归调用MemberwiseClone()来实现。此方法适用于我关心的任何对象,包括层次结构关系对象。该方法还具有过去访问的字典,因此避免了不必要的重复工作。

然而,我遇到的问题是,只有在需要克隆时对象不是绑定到WPF / MVVM的数据时,此方法才有效。当数据绑定并调用方法时,我会遇到堆栈溢出异常,因为(我假设)INotifyPropertyChanged.PropertyChanged事件和WPF框架之间建立了链接。递归调用然后尝试复制整个对象世界,包括AppDomain和低级Pointer对象,这些对象看起来是链接的,并且几乎无限远(无论如何,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;
}

1 个答案:

答案 0 :(得分:0)

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

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