65.9K
CodeProject 正在变化。 阅读更多。
Home

浅拷贝和深拷贝对象

starIconstarIconstarIconstarIconstarIcon

5.00/5 (3投票s)

2009年5月1日

CPOL

4分钟阅读

viewsIcon

43910

浅拷贝和深拷贝对象

在 .NET 中,类对象是引用类型。 将一个对象变量分配给另一个对象变量并不会复制该对象,而只是使两个对象变量引用同一个对象。

有时,需要进行复制。 例如,可能两个例程需要以相同的数据开始,然后彼此独立地更改该数据。 复制数据可确保一个例程所做的更改不会影响另一个例程正在使用的数据。

使用 .NET 时,可以进行两种类型的复制:浅拷贝和深拷贝。 在浅拷贝的情况下,会创建一个新对象,并将原始对象的每个成员分配给新对象的相应成员。 对于值成员,这才是真正的复制。 但是,对于包含引用成员的对象,这不会生成真正的副本。

引用类型的一个例子是 string。 当您将一个 string 变量分配给另一个变量时,两个变量都将引用相同的 string 数据。 string 的字符实际上并未被复制。 因此,如果一个类包含引用成员,则浅拷贝不会创建所有类成员的真正副本。

在许多情况下,浅拷贝就足够了。 请注意,string 是不可变的,无法更改。 当您创建包含 string 的对象的浅拷贝,然后修改新对象中的 string 时,这将创建一个新的 string,并且不会对原始对象中的原始 string 产生任何影响。 请注意,其他数据类型(如数组、类对象和类对象数组)可能比 string 复杂得多。

深拷贝是指创建的副本不包含任何原始数据。 创建每个成员的真实副本。 深拷贝不需要对值类型的成员执行任何特殊操作。 但是对于引用数据类型,新对象必须引用该数据的副本,而不是原始数据。

执行对象复制的两种方法没有什么特别之处。 考虑一下清单 1。此代码声明了一个名为 MyClass 的类,然后显示了一个名为 Test 的简短方法,该方法使用该 class 对象执行浅拷贝和深拷贝。

protected class MyClass
{
   public int i;
   public int j;
   public string message;
}
private void Test()
{
   MyClass mc1;
   MyClass mc2;
   mc1 = new MyClass();
   mc1.i = 5;
   mc1.j = 10;
   mc1.message = "Hello, World!";

   // Shallow copy
   mc2 = new MyClass();
   mc2.i = mc1.i;
   mc2.j = mc1.j;
   mc2.message = mc1.message;

   // Deep copy
   mc2 = new MyClass();
   mc2.i = mc1.i;
   mc2.j = mc1.j;
   mc2.message = String.Copy(mc1.message);
}
清单 1:对象的浅拷贝和深拷贝。

浅拷贝不执行任何特殊操作。 它只是将一个对象的每个成员分配给另一个对象。 对于值成员,深拷贝使用相同的代码。 但是,对于一个引用成员 message,代码必须创建 string 数据的副本。(请注意,要使用包含对其他对象的引用的引用成员(例如类成员、数组等)的对象执行深拷贝,还需要额外的步骤。)

现在我已经希望解释了浅拷贝和深拷贝之间的区别,让我们看看 .NET Framework 提供的一些执行这些任务的工具。

protected class MyClass : ICloneable
{
    public int i;
    public int j;
    public string message;
    public object Clone()
    {
        return MemberwiseClone();
    }
}

private void Test()
{
    MyClass mc1 = new MyClass();
    mc1.i = 5;
    mc1.j = 10;
    mc1.message = "Hello, World!";

    // Shallow copy
    MyClass mc2 = (MyClass)mc1.Clone();
}
清单 2:使用 MemberwiseClone() 执行浅拷贝。

清单 2 使用 MemberwiseClone() 执行浅拷贝。 MemberwiseClone()protected 的,因此不能直接从 Test 调用。 相反,我修改了 MyClass 以实现 ICloneable interface 并实现了 ICloneable 方法 Clone。(通常,ICloneable 与深拷贝相关联,但我在这里使用它来实现浅拷贝。) Test 方法调用这个新方法来执行浅拷贝。 由于 Clone() 返回 type 对象,因此需要进行类型转换。

要执行深拷贝,清单 3 还实现了 ICloneable 接口。 此清单仅修改 Clone() 方法中的代码以执行深拷贝。

protected class MyClass : ICloneable
{
    public int i;
    public int j;
    public string message;
    public object Clone()
    {
        MyClass mc = new MyClass();
        mc.i = i;
        mc.j = j;
        if (message != null)
            mc.message = String.Copy(message);
        return mc;
    }
}

private void Test()
{
    MyClass mc1 = new MyClass();
    mc1.i = 5;
    mc1.j = 10;
    mc1.message = "Hello, World!";

    // Deep copy
    MyClass mc2 = (MyClass)mc1.Clone();
}
清单 3:使用 ICloneable 执行深拷贝。

现在应该熟悉 Clone() 方法中的实际代码。 以这种方式实现它的主要优点是它作为类的一部分实现,可以在应用程序中的任何位置轻松修改和调用。

这里没有什么太复杂的,尽管浅拷贝和深拷贝背后的概念可能会让一些人感到困惑。 希望我已经阐明了这个主题,并演示了如何使用 .NET 来处理这个问题。

© . All rights reserved.