如何在没有序列化的情况下在 C# 中创建真正的深度副本?

How to Create True Deep Copies in C# Without Serialization?

提问人:underloaded_operator 提问时间:10/17/2023 更新时间:10/17/2023 访问量:92

问:

我正在阅读《丁格尔,A.》(2021 年)一书。章节:在 C# 中克隆。在《面向对象的设计选择》(第 71-73 页)中,CRC Press“,在有关 C# 中的克隆的章节中,它展示了如何通过类制作对象的深度和浅层副本。UCopy

我很难理解这一切是如何运作的。我在网上找到了一些博客,它说很难在 C# 中制作真正的深度副本(没有 Serialize 和 Deserialize 方法)。我正在努力在我的作业中实现这一点,我无法围绕这个过程。

  • 我制作的每个类都需要一个类吗(是只复制一个类,还是应该是通用的)UCopy
  • 它是否创建了真正的深层副本?我可能是错的,但据我所知,这不是一个真正的深层拷贝,因为,就像 中一样,AnotherClass 对象是不同的,但值并不是真正不同的,因为它是从同一个 初始化的。u4u3Ownstatic id
  • 该方法从何而来?Clone

这是书中的代码示例(图 3.5)

public class AnotherClass
{
    private static uint id = 1000;
    private readonly uint own;

    public AnotherClass()
    {
        own = id++;
    }

    public uint Own => own;
}

public class UCopy
{
    private AnotherClass address;

    public UCopy()
    {
        address = new AnotherClass();
    }

    // Deep copy: heap memory allocated for a true copy
    public UCopy DeepCopy()
    {
        // Internal cast
        UCopy local = (UCopy)this.MemberwiseClone();
        local.address = new AnotherClass();
        return local;
    }

    // Shallow copy: distinct objects have the same address
    public UCopy ShallowCopy()
    {
        return (UCopy)this.MemberwiseClone();
    }
}

public class Program
{
    public static void Main()
    {
        // Client code
        // #1: UCopy object allocated
        UCopy u1 = new UCopy();

        // Embedded 'Own' id of 1000

        // #2: Shallow copy, aliased with u1
        UCopy u2 = u1.ShallowCopy();

        // Embedded 'Own' id of 1000

        // #3: Shallow copy, aliased with u1
        UCopy u3 = u1.DeepCopy();

        // Embedded 'Own' id of 1000

        // #4: Deep copy - DISTINCT object
        UCopy u4 = u1.DeepCopy();

        // Embedded 'Own' id of 1001
    }
}
C# 克隆

评论


答:

1赞 Enigmativity 10/17/2023 #1

如果使用,你会掉进一个很深的兔子洞,因为它真的不清楚你需要修改什么才能制作一个深度克隆。MemberwiseClone()

你最好编写我们自己的深度克隆方法并显式更新所有内容。

下面是一个示例:

public class Class1
{
    public Class2 Class2 { get; init; }
    public string Id { get; set; }

    public Class1 DeepCopy() => new Class1()
    {
        Id = this.Id,
        Class2 = this.Class2.DeepCopy()
    };
}

public class Class2
{
    public Class3 Class3 { get; init; }
    public string Id { get; set; }

    public Class2 DeepCopy() => new Class2()
    {
        Id = this.Id,
        Class3 = this.Class3.DeepCopy()
    };
}

public class Class3
{
    public string Id { get; set; }
    public Class3 DeepCopy() => new Class3()
    {
        Id = this.Id
    };
}

不过,我认为您最好在每个类上编写静态方法来进行克隆:

public class Class1
{
    public Class2 Class2 { get; init; }
    public string Id { get; init; }

    public static Class1 DeepClone(Class1 clone) => new Class1()
    {
        Id = clone.Id,
        Class2 = Class2.DeepClone(clone.Class2)
    };
}

public class Class2
{
    public Class3 Class3 { get; init; }
    public string Id { get; init; }

    public static Class2 DeepClone(Class2 clone) => new Class2()
    {
        Id = clone.Id,
        Class3 = Class3.DeepClone(clone.Class3)
    };
}

public class Class3
{
    public string Id { get; init; }
    public static Class3 DeepClone(Class3 clone)
        => new Class3() { Id = clone.Id };
}

然后你会这样写:

Class1 object1 = new Class1()
{
    Id = "C1",
    Class2 = new Class2()
    {
        Id = "C2",
        Class3 = new Class3()
        {
            Id = "C3",
        }
    }
};

Class1 object1c = Class1.DeepClone(object1);

无论哪种情况,您都可以 100% 控制深度克隆过程,并且您正在做什么是明确的。

评论

0赞 underloaded_operator 10/17/2023
谢谢!这绝对更有意义