【问题标题】:Shallow copy or Deep copy?浅拷贝还是深拷贝?
【发布时间】:2013-08-05 19:45:20
【问题描述】:

我对这两种将一个对象复制到另一个对象的方法有点陌生。我很困惑,无法找出深拷贝和浅拷贝之间的主要区别。我已经经历了很多关于这个的理论,但我需要用适当的例子来解释。 我有一个程序,我在其中将一个对象复制到另一个对象中。 -->

   class A
    {
        public int a = 0;
        public void display()
        {
            Console.WriteLine("The value of a is " + a);
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            A ob1 = new A();
            ob1.a = 10;
            ob1.display();
            A ob2 = new A();
            ob2 = ob1;
            ob2.display();
            Console.Read();
        }
    }

这是浅拷贝还是深拷贝?任何人都可以提供理由的答案。如果是深拷贝,请提供该程序的浅拷贝代码,该程序执行相同的对象复制工作,反之亦然..

如果上面是浅拷贝,那么即使这个也应该是浅拷贝-->

            A ob1 = new A();
            ob1.a = 10;
            ob1.display();
            A ob2 = ob1;
            ob2.a = 444;
            ob1.display();

【问题讨论】:

  • 这是一个浅拷贝。
  • 关于对象复制的维基百科页面:en.wikipedia.org/wiki/Object_copy
  • @VaughanHilts - 我不会称它为“浅拷贝”,因为上面的代码根本不执行 obj1 的任何拷贝......
  • 需要副本是代码异味,也许你应该在这种情况下使用不可变类型
  • 通过A ob2 = new A(); 创建的对象几乎立即被丢弃。下一行使ob2 成为与ob1 相同对象的引用,而new A() 在被垃圾回收之前会丢失。

标签: c# deep-copy shallow-copy


【解决方案1】:

来自链接here

浅拷贝尽可能少地重复。 a的浅拷贝 集合是集合结构的副本,而不是元素。 使用浅拷贝,两个集合现在共享个人 元素。

深拷贝复制一切。一个集合的深拷贝是两个 包含原始集合中所有元素的集合 重复。

你的例子是创建一个浅拷贝。

A ob1 = new A();
ob1.a = 10;
A ob2 = new A();
ob2 = ob1;

ob1.a = 5; // <-- If you see value of ob2.a after this line, it will be 5.

深拷贝将是 -

 A ob1 = new A();
 ob1.a = 10;
 A ob2 = new A();
 ob2.a = ob1.a;

 ob1.a = 5; // <-- If you see value of ob2.a after this line, it will be 10.

【讨论】:

  • 我找不到你。 '因为您没有将 ob1 分配给 ob2,而是为变量 a..
  • 通过将 ob2 分配给 ob1 您指的是同一个实例,即在 ob1 字段中的任何更新都会反映在 ob2 字段中。
  • 但是,ob1 和 ob2 有自己不同的内存分配,因为我已经具体实例化了它们。. ob2 如何依赖并指向 ob1 的内存分配,而不是 ob2 应该复制数据来自ob1的内存地址,而不是指向它..
  • 你搞错了内存分配的概念。当您创建两个实例时,单独的内存分配完成指向那里自己的引用。但是,一旦将它们等同起来,它们就会开始指向相同的参考。详细解释参考这里-stackoverflow.com/questions/7477073/…
  • 哦.. 现在我明白了。非常感谢!!
【解决方案2】:

在我看来,它不是严格的浅拷贝或深拷贝。如果非要定义的话,我会说浅拷贝。

ob2 = ob1; 这段代码创建了两个引用同一个对象的对象引用。因此,通过 ob1 对对象所做的任何更改都会反映在 ob2 的后续使用中。

来自 MSDN 的示例将更好地解释浅拷贝、深拷贝和只是简单的类拷贝的区别。

 using System;

    public class IdInfo
    {
        public int IdNumber;

        public IdInfo(int IdNumber)
        {
            this.IdNumber = IdNumber;
        }
    }

    public class Person
    {
        public int Age;
        public string Name;
        public IdInfo IdInfo;

        public Person ShallowCopy()
        {
            return (Person)this.MemberwiseClone();
        }

        public Person DeepCopy()
        {
            Person other = (Person)this.MemberwiseClone();
            other.IdInfo = new IdInfo(this.IdInfo.IdNumber);
            other.Name = String.Copy(this.Name);
            return other;
        }
    }

    public class Example
    {
        public static void Main()
        {
            // Create an instance of Person and assign values to its fields.
            Person p1 = new Person();
            p1.Age = 42;
            p1.Name = "Sam";
            p1.IdInfo = new IdInfo(6565);

            // Perform a shallow copy of p1 and assign it to p2.
            Person p2 = (Person)p1.ShallowCopy();

            // Display values of p1, p2
            Console.WriteLine("Original values of p1 and p2:");
            Console.WriteLine("   p1 instance values: ");
            DisplayValues(p1);
            Console.WriteLine("   p2 instance values:");
            DisplayValues(p2);

            // Change the value of p1 properties and display the values of p1 and p2.
            p1.Age = 32;
            p1.Name = "Frank";
            p1.IdInfo.IdNumber = 7878;
            Console.WriteLine("\nValues of p1 and p2 after changes to p1:");
            Console.WriteLine("   p1 instance values: ");
            DisplayValues(p1);
            Console.WriteLine("   p2 instance values:");
            DisplayValues(p2);

            // Make a deep copy of p1 and assign it to p3.
            Person p3 = p1.DeepCopy();
            // Change the members of the p1 class to new values to show the deep copy.
            p1.Name = "George";
            p1.Age = 39;
            p1.IdInfo.IdNumber = 8641;
            Console.WriteLine("\nValues of p1 and p3 after changes to p1:");
            Console.WriteLine("   p1 instance values: ");
            DisplayValues(p1);
            Console.WriteLine("   p3 instance values:");
            DisplayValues(p3);

            // Make an equal of p1 and assign it to p4.
            Person p4 = new Person();
            p4 = p1;
            // Change the members of the p1 class to new values to show the equal copy.
            p1.Name = "Will";
            p1.Age = 30;
            p1.IdInfo.IdNumber = 8484;
            Console.WriteLine("\nValues of p1 and p4 after changes to p1:");
            Console.WriteLine("   p1 instance values: ");
            DisplayValues(p1);
            Console.WriteLine("   p4 instance values:");
            DisplayValues(p4);
        }

        public static void DisplayValues(Person p)
        {
            Console.WriteLine("      Name: {0:s}, Age: {1:d}", p.Name, p.Age);
            Console.WriteLine("      Value: {0:d}", p.IdInfo.IdNumber);
        }
    }

结果如下:

Original values of p1 and p2:    p1 instance values:
      Name: Sam, Age: 42
      Value: 6565    p2 instance values:
      Name: Sam, Age: 42
      Value: 6565

Values of p1 and p2 after changes to p1:    p1 instance values:
      Name: Frank, Age: 32
      Value: 7878    p2 instance values:
      Name: Sam, Age: 42
      Value: 7878

Values of p1 and p3 after changes to p1:    p1 instance values:
      Name: George, Age: 39
      Value: 8641    p3 instance values:
      Name: Frank, Age: 32
      Value: 7878

Values of p1 and p4 after changes to p1:    p1 instance values:
      Name: Will, Age: 30
      Value: 8484    p4 instance values:
      Name: Will, Age: 30
      Value: 8484

【讨论】:

  • String.Copy(this.Name) 根本不需要。在 C# 中(同样在 Java 中)字符串是不可变的,因此原始和深度复制可以共享字符串。
【解决方案3】:

这既不是浅拷贝也不是深拷贝,这是一个引用拷贝。让我解释一下:变量有两种类型:值类型和引用类型。

值类型是计算机内存中的(命名)位置,用于保存变量的实际值。比如:int是值类型,所以写这行代码的时候:

int MyInt = 5;

当这行代码被执行时,运行时将在 RAM 中找到一个位置并将值 5 写入其中。因此,如果您搜索该位置,您会发现实际值为 5。

引用类型 - 相比之下 - 是内存中的(命名)位置,它实际上并不保存变量的值,而是保存该值所在的内存位置。例如,假设您编写了以下代码:

MyClass myObject = new MyClass();

虚拟机(运行时)会发生什么: 1-查看并找到内存中的可用位置,创建 MyClass 类的实例。假设该对象的位置恰好位于 RAM 中的字节#AA3D2。

2- 在内存中找到一个位置并创建 MyClass 类型的引用(引用是指向内存中某个位置的“箭头”),将其命名为“myObject”并将值 AA3D2 存储在其中。

现在,如果您查看“myObject”变量,您会发现不是类实例,但您会发现 AA3D2,它表示保存该类实例的内存位置。

现在让我们检查给定我的 OP 的代码:

A ob1 = new A();

这将创建一个名为 ob1 的变量,创建 A 类的实例并将该类的位置存储在 ob1 中

ob1.a = 10;
ob1.display();

这将改变 A 类中的变量 a。然后调用 display() 方法

A ob2 = new A();

在这里它创建一个名为 ob2 的变量,创建一个类 A 的实例并将其位置分配给 ob2。

现在您在内存中有 2 个 A 类实例和 2 个变量,每个变量都指向其中一个。 现在这是有趣的部分: ob2 = ob1;

变量 ob2 被赋予变量 ob1 的值。因为 ob1 包含 A 的第一个实例的内存位置,所以现在 ob1 和 ob2 都指向内存中的相同位置。使用其中一个做任何事情都与另一个做同样的事情。

ob2 = ob1 表示您正在复制引用。

【讨论】:

    【解决方案4】:

    这是一个浅拷贝,因为如果您修改 ob2 的变量 - 然后尝试打印 ob1 - 它们将是相同的。这是因为 C# 中作为类的东西在它们之间创建了链接。如果你想做一个深拷贝,你应该实现一个拷贝方法并手动拷贝字段。比如:

      class A
        {
            public int a = 0;
            public void display()
            {
                Console.WriteLine("The value of a is " + a);
            }
    
           public A Copy()
        {
            A a = new A();
            a.a = = this.a;
            return a;
        }
    
    
    
        }
    

    【讨论】:

      【解决方案5】:

      我赞同@docesam 的回答和@Will Yu 的部分回答。

      这既不是浅拷贝也不是深拷贝,这是参考拷贝。 -- 文档


      ob2 = ob1;此代码创建两个对象引用,它们都引用同一个对象。因此,通过 ob1 对对象所做的任何更改都会反映在 ob2 的后续使用中。 --于将


      根据MSDN (see Remarks)

      Array 的浅拷贝只复制 Array 的元素,无论它们是引用类型还是值类型,但它不会复制引用所引用的对象。新 Array 中的引用指向的对象与原 Array 中的引用指向的对象相同。

      这里有两点需要注意:

      1. 浅拷贝复制元素。
      2. 浅拷贝保留元素的原始引用。

      接下来,让我分别解释这两个。


      首先,我们创建一个带有Name 属性的Person 类:

      class Person
      {
          public string Name {get; set;}
      }
      

      然后在Main()方法中,我们创建一个Person数组。

      // Create 2 Persons.
      var person1 = new Person(){ Name = "Jack" };
      var person2 = new Person(){ Name = "Amy" };
      
      // Create a Person array.
      var arrPerson = new Person[] { person1, person2 };
      

      1.浅拷贝复制元素。

      如果我们替换浅拷贝中的第一个元素,原数组应该不会受到影响:

      // Create a shallow copy.
      var arrPersonClone = (Person[]) arrPerson.Clone();
      
      // Replace an element in the shallow copy.
      arrPersonClone[0] = new Person(){Name = "Peter"};
      
      // Display the contents of all arrays.
      Console.WriteLine( "After replacing the first element in the Shallow Copy" );
      Console.WriteLine( $"The Original Array: {arrPerson[0].Name}, {arrPerson[1].Name}" );
      Console.WriteLine( $"The Shallow Copy: {arrPersonClone[0].Name}, {arrPersonClone[1].Name}" );
      

      结果:

      The Original Array: Jack, Amy
      The Shallow Copy: Peter, Amy
      

      2。浅拷贝保留元素的原始引用。

      如果我们在浅拷贝中更改元素的属性,原始数组将受到影响,因为该元素引用的对象没有被复制。

      // Create a new shallow copy.
      arrPersonClone = (Person[]) arrPerson.Clone();
      
      // Change the name of the first person in the shallow copy.
      arrPersonClone[0].Name = "Peter";
      
      // Display the contents of all arrays.
      Console.WriteLine( "After changing the Name property of the first element in the Shallow Copy" );
      Console.WriteLine( $"The Original Array: {arrPerson[0].Name}, {arrPerson[1].Name}" );
      Console.WriteLine( $"The Shallow Copy: {arrPersonClone[0].Name}, {arrPersonClone[1].Name}" );
      

      结果:

      The Original Array: Peter, Amy
      The Shallow Copy: Peter, Amy
      

      那么简单的等号= 的行为如何?

      它制作一个参考副本。对元素或引用对象的任何更改都将反映在原始数组和“复制”数组中。

      // Create a reference copy.
      var arrPersonR = arrPerson;
      
      // Change the name of the first person.
      arrPersonR[0].Name = "NameChanged";
      // Replace the second person.
      arrPersonR[1] = new Person(){ Name = "PersonChanged" };
      
      // Display the contents of all arrays.
      Console.WriteLine( "After changing the reference copy:" );
      Console.WriteLine( $"The Original Array: {arrPerson[0].Name}, {arrPerson[1].Name}" );
      Console.WriteLine( $"The Reference Copy: {arrPersonR[0].Name}, {arrPersonR[1].Name}" );
      

      结果:

      The Original Array: NameChanged, PersonChanged
      The Reference Copy: NameChanged, PersonChanged
      

      总结ob2 = ob1 不是浅拷贝,而是参考拷贝。

      完整代码:

      void Main()
      {
          // Create 2 Persons.
          var person1 = new Person(){ Name = "Jack" };
          var person2 = new Person(){ Name = "Amy" };
      
          // Create a Person array.
          var arrPerson = new Person[] { person1, person2 };
      
          // ----------- 1. A shallow copy copies elements. -----------
      
          // Create a shallow copy.
          var arrPersonClone = (Person[]) arrPerson.Clone();
      
          // Replace an element in the shallow copy.
          arrPersonClone[0] = new Person(){Name = "Peter"};
      
          // Display the contents of all arrays.
          Console.WriteLine( "After replacing the first element in the Shallow Copy:" );
          Console.WriteLine( $"The Original Array: {arrPerson[0].Name}, {arrPerson[1].Name}" );
          Console.WriteLine( $"The Shallow Copy: {arrPersonClone[0].Name}, {arrPersonClone[1].Name}" );
      
          Console.WriteLine( "\n" );
      
          // ----------- 2. A shallow copy retains the original references of the elements. -----------
      
          // Create a new shallow copy.
          arrPersonClone = (Person[]) arrPerson.Clone();
      
          // Change the name of the first person in the shallow copy.
          arrPersonClone[0].Name = "Peter";
      
          // Display the contents of all arrays.
          Console.WriteLine( "After changing the Name property of the first element in the Shallow Copy:" );
          Console.WriteLine( $"The Original Array: {arrPerson[0].Name}, {arrPerson[1].Name}" );
          Console.WriteLine( $"The Shallow Copy: {arrPersonClone[0].Name}, {arrPersonClone[1].Name}" );
      
          Console.WriteLine( "\n" );  
      
          // ----------- 2. The equal sign. -----------
      
          // Create a reference copy.
          var arrPersonR = arrPerson;
      
          // Change the name of the first person.
          arrPersonR[0].Name = "NameChanged";
          // Replace the second person.
          arrPersonR[1] = new Person(){ Name = "PersonChanged" };
      
          // Display the contents of all arrays.
          Console.WriteLine( "After changing the reference copy:" );
          Console.WriteLine( $"The Original Array: {arrPerson[0].Name}, {arrPerson[1].Name}" );
          Console.WriteLine( $"The Reference Copy: {arrPersonR[0].Name}, {arrPersonR[1].Name}" );
      }
      
      class Person
      {
          public string Name {get; set;}
      }
      

      【讨论】:

        【解决方案6】:

        在将第一个对象的属性分配给第二个对象后,再编写几行代码来更改它的属性。然后在两个对象上调用 display 方法,看看结果是什么。这将向您揭示它实际上是一个浅拷贝。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2012-01-30
          • 2011-10-13
          • 1970-01-01
          • 1970-01-01
          • 2010-11-19
          • 2013-08-23
          • 1970-01-01
          相关资源
          最近更新 更多