2017-09-14 47 views
-1

我创建了一个ArrayList,我克隆它,并添加一个元素到原始,它不会显示在克隆中。根据VS文档,克隆执行浅拷贝。为什么Clone()似乎执行深度复制?

using System.Collections; 
using System; 
namespace WhatDoesCloneDo 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      ArrayList a, b; 
      a = new ArrayList(); 
      a.Add("Chocolate"); 
      a.Add("Vanilla"); 
      a.Add("Crumb"); 

      b = (ArrayList) a.Clone(); 
      // What's in b and a? 
      a.Add("Cheese"); 
      Console.WriteLine(a.Count); 
      Console.WriteLine(b.Count); 
     } 
    } 
} 

这是输出

4 
3 
Press any key to continue . . . 
+0

它不是深层复制,只有集合被克隆,而不是其元素。但是当你使用不可变的字符串时你看不到。克隆()具有如此不可预知性是List <>没有它的一个原因。 –

+0

你不知道浅层和深层克隆意味着什么。你应该去看看他们。 – Will

+0

@非常感谢!很有帮助。 – nicomp

回答

1

当你Clone,你有一个新的对象,它是不一样的参考。

在您添加的示例中,您将克隆a a生成一个指向参考b的新实例ArrayList。您在a添加了一个新元素,与b不一样。这就是你输出的原因:

4 
3 

如果你做这样的事情:

ArrayList b = a; 

你是不是克隆的对象。它将在参考文献ab上提供相同的对象。你会得到输出:

4 
4 

ArrayList.Clone MSDN文档:

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

1

浅拷贝仍然是拷贝,因此列表的第二个实例。将数组添加到数组a不会将其添加到数组b

什么是浅拷贝意味着参考每个数组引用相同的实例。深层副本将为阵列中的每个项目创建新实例。

0

因为克隆不是参考,克隆攻击中的克隆不是Boba Fett的浅拷贝。

0

你有副本(ArrayList项目将不可克隆):

var a = new ArrayList(); 

    a.Add("Chocolate"); 
    a.Add("Vanilla"); 
    a.Add("Crumb"); 

    var b = (ArrayList) a.Clone(); 

    bool deep = false; 

    for (int i = 0; i < a.Count; ++i) 
    if (!object.ReferenceEquals(a[i], b[i])) { 
     // If we have at least one b[i] instance 
     // which doesn't share the corresponding a[i] reference 
     // we can suspect that we perform deep copy 
     deep = true; 

     break; 
    } 

    Console.Write(deep ? "Deep" : "Shallow"); 

最后,让我们在ArrayList.Clone()源代码一个llok:

https://referencesource.microsoft.com/#mscorlib/system/collections/arraylist.cs,46e5b8aaeb6679f2

// Clones this ArrayList, doing a shallow copy. (A copy is made of all 
    // Object references in the ArrayList, but the Objects pointed to 
    // are not cloned). 
    public virtual Object Clone() 
    { 
     Contract.Ensures(Contract.Result<Object>() != null); 
     ArrayList la = new ArrayList(_size); 
     la._size = _size; 
     la._version = _version; 
     Array.Copy(_items, 0, la._items, 0, _size); 
     return la; 
    } 

为了找出Clone浅拷贝

0

浅度副本创建的对象ArrayList的副本与原始相同的参考。

集合中的引用是共享的,因此它不是“深层”的副本。

public class Food 
{ 
    public Food(string name) 
    { 
     Name = name; 
    } 

    public string Name { get; set; } 

    public override string ToString() 
    { 
     return Name; 
    } 
} 

ArrayList a, b; 
a = new ArrayList(); 
a.Add(new Food("Cocoa")); 

b = (ArrayList)a.Clone(); 

// a, b now have "Chocolate" instead of "Cocoa" 
((Food)b[0]).Name = "Chocolate"; 

Console.WriteLine(a[0]); // Prints "Chocolate" 
Console.WriteLine(b[0]); // Prints "Chocolate" 
0

看起来你可能会误认为浅拷贝和深拷贝之间的区别。

在浅拷贝中,将创建一个新对象,并且将复制原始成员的所有值。

ArrayList a = new ArrayList(); // Creates a reference variable and a new 
           // object and places a reference to it in a 
ArrayList b = a; // Creates a new reference variable and copies the 
       // reference in a to b. They both refer to the same new ArrayList(); 

b = (ArrayList)a.Clone(); // Clone() creates a new object and copies the 
          // values from a into it, then assigns the reference to b. 
          // a and b now refer to different objects with the same values 

当处理值类型和字符串不可变时,这一切都非常简单。当它开始与明显不同的是,当你开始处理参考类型的成员时。

不是像原来的文章那样分配字符串,而是创建一个简单的Person类,我们将通过该场景中发生的事情。

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

ArrayList a = new ArrayList(); // New object, reference in a 
a.Add(new Person(){Name = "Jack"}); // New person object, reference added to a. 
a.Add(new Person(){Name = "Jill"}); // New person object, reference added to a. 

ArrayList b = (ArrayList)a.Clone(); // New ArrayList object created, references 
            // to both Person objects are copied to it, 
            // then the reference to the new ArrayList 
            // is stored in b. 

此时,a和b都引用不同的ArrayList对象,它们引用了相同的Person对象。浅拷贝使得每个成员的的副本,并且该值是对Person对象的引用,而不是对象本身。

正因为如此,一个Person对象所做的任何更改将反映在A和B:

Console.WriteLine(((Person)a[0]).Name); // Prints "Jack" 
Console.WriteLine(((Person)b[0]).Name); // Prints "Jack" 
((Person)a[0]).Name = "Not Jack"; // Changes the value of the object, which is 
            // referenced by both a and b. 

Console.WriteLine(((Person)a[0]).Name); // Prints "Not Jack" 
Console.WriteLine(((Person)b[0]).Name); // Prints "Not Jack" 

这是一个浅拷贝的行为。

深度复制行为有点不同。当您在ArrayList上执行浅表复制时,它会创建一个新的ArrayList对象并复制原始值。当它是一个深层副本时,它将创建一个新的ArrayList对象,就像在浅拷贝中一样,但不是仅仅处理这些值,而是递归地对每个成员执行一个复制操作。

这里是在幕后进行了深刻的副本背后发生的事情一个简单的解释:

  1. 创建一个新的ArrayList对象。
  2. 浏览原始对象中的所有成员,并确定它是否为值类型或引用类型。
    • 如果它是一个值的类型,它只是在价值
    • 如果它是一个引用类型的副本,从第1步,但这种类型的开始。

一旦操作完成,您现在可以使a或b改变任何东西,而不影响其他ArrayList中的对象。