如何将深度克隆元素重新附加到引用树?

时间:2015-11-04 18:10:29

标签: c# reference deep-copy cloning

今天我在C#中使用引用时遇到了问题。我的用例如下:我在winform-application中有一个全局配置对象。打开设置屏幕时,对象的设置部分会被深度克隆。单击保存按钮时,元素应保存回原始参考。这不像我最初的预期那样有效。

我准备了一个网络小提琴,让你看一下这个问题。如果有人知道解决这个问题的方法,我非常感谢你的意见。我很感谢指出我的修正(我确信有人以更清洁的方式解决了这个问题)。

1)我们有一个全局可用的静态对象结构

2)在某个时间点,该结构的一部分应该从对象结构中脱离

3)将对已删除的部分进行编辑

4)稍后,应将MAYBE部分重新附加到对象结构中的原始引用

我是一名前端开发人员,很难用文字表达我的问题,所以看看我的代码:

https://dotnetfiddle.net/qzJqC4

---对于来自遥远未来的读者来说,链接已成为过去---

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;

namespace DemoCloneAndReattach
{
[Serializable]
public class DogFamily
{
public DogFamily()
{
    PrettyDogs = new List<PrettyDog>();
}

public List<PrettyDog> PrettyDogs { get; set; }
}

[Serializable]
public class PrettyDog
{
    public string Name { get; set; }
    public int NumberOfEars { get; set; }
}

public class Program
{
    public static void Main(string[] args)
    {
        // Note: The WriteLines will help you to validate the correctness of the solution. Thanks a lot!

        // Setting up
        DogFamily dogFamily = new DogFamily();

        dogFamily.PrettyDogs.Add(new PrettyDog() { Name = "OriginalDog", NumberOfEars = 2 });

        // Getting the dog with the name "OriginalDog"
        PrettyDog originalDog = dogFamily.PrettyDogs.FirstOrDefault(o => o.Name == "OriginalDog");
        originalDog.NumberOfEars = 3;
        Console.WriteLine("originalDog has NumberOfEars (expected 3): " + originalDog.NumberOfEars);

        // Checking if the originalDog in the List has the value updated (from 2 to 3). It does.
        PrettyDog checkDogOne = dogFamily.PrettyDogs.FirstOrDefault(o => o.Name == "OriginalDog");
        Console.WriteLine();
        Console.WriteLine("checkDogOne has NumberOfEars (expected 3): " + checkDogOne.NumberOfEars);

        // Now creating a deep-clone of the originalDog (this will result in a brand new object, not related to the DogFamily in any way)
        PrettyDog clonedDog = DeepClone<PrettyDog>(originalDog);

        // Doing something that should not YET be written to the DogFamily-reference tree.
        clonedDog.NumberOfEars = 7;

        // Checking if the clonedDog has not the same reference as the originalDog. As expected, it hasn't.
        Console.WriteLine();
        Console.WriteLine("clonedDog has NumberOfEars (expected 7): " + clonedDog.NumberOfEars);
        Console.WriteLine("originalDog still has NumberOfEars (expected 3): " + originalDog.NumberOfEars);

        // I want the behavior below, but automated (some kind of reverse-matching or -reattaching-logic to the reference of originalDog):
        originalDog.Name = clonedDog.Name;
        originalDog.NumberOfEars = clonedDog.NumberOfEars;

        // Maybe the  call to the solution would look like this:
        // Reattach<PrettyDog>(originalDog, clonedDog);

        Console.WriteLine();
        Console.WriteLine("originalDog now has NumberOfEars (expected 7): " + originalDog.NumberOfEars);
        Console.WriteLine("clonedDog has still NumberOfEars (expected 7): " + clonedDog.NumberOfEars);

        // Checking if the reference is set correctly (not only the originalDog-reference but the whole reference-tree)
        PrettyDog checkDogTwo = dogFamily.PrettyDogs.FirstOrDefault(o => o.Name == "OriginalDog");
        Console.WriteLine();
        Console.WriteLine("checkDogTwo has NumberOfEars (expected 7 - this is the tricky one): " + checkDogTwo.NumberOfEars);
    }

    public static T DeepClone<T>(T obj)
    {
        if (obj == null) return default(T);

        using (var ms = new MemoryStream())
        {
            var formatter = new BinaryFormatter();
            formatter.Serialize(ms, obj);
            ms.Position = 0;

            return (T)formatter.Deserialize(ms);
        }
    }

    public static void Reattach<T>(T original, T clone) 
    { 
        // Logic for replacing the original with the clone without damaging the reference-tree
    }
}
}

0 个答案:

没有答案