为什么Clone()似乎执行深层复制?

时间:2017-09-14 20:43:33

标签: c#

我创建一个ArrayList,我克隆它,并在原始元素中添加一个元素,它似乎不会显示在克隆中。根据VS文档,Clone执行浅拷贝。

def _do_overwrite(fs, path, copy_data):
    """
    Atomically (best-effort) save the specified data to the given path
    on the filesystem.
    """
    # TODO(todd) Should probably do an advisory permissions check here to
    # see if we're likely to fail (eg make sure we own the file
    # and can write to the dir)

    # First write somewhat-kinda-atomically to a staging file
    # so that if we fail, we don't clobber the old one
    path_dest = path + "._hue_new"

    # Copy the data to destination
    copy_data(path_dest)

    # Try to match the permissions and ownership of the old file
    cur_stats = fs.stats(path)
    try:
        fs.do_as_superuser(fs.chmod, path_dest, stat_module.S_IMODE(cur_stats['mode']))
    except:
        logging.exception("Could not chmod new file %s to match old file %s" % (path_dest, path))
        # but not the end of the world - keep going

    try:
        fs.do_as_superuser(fs.chown, path_dest, cur_stats['user'], cur_stats['group'])
    except:
        logging.exception("Could not chown new file %s to match old file %s" % (path_dest, path))
        # but not the end of the world - keep going

    # Now delete the old - nothing we can do here to recover
    fs.remove(path, skip_trash=True)

    # Now move the new one into place
    # If this fails, then we have no reason to assume
    # we can do anything to recover, since we know the
    # destination shouldn't already exist (we just deleted it above)
    fs.rename(path_dest, path)

这是输出

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);
        }
    }
}

6 个答案:

答案 0 :(得分:1)

当你Clone时,你有一个新对象,它不是同一个引用。

在您添加的示例中,您克隆a a生成一个指向引用ArrayList的新b实例。您在a处添加了一个新元素,该元素与b不同。这就是你获得输出的原因:

4
3

如果您这样做:

ArrayList b = a;

您没有克隆对象。它将在引用ab上提供相同的对象。你会得到输出:

4
4

来自ArrayList.Clone MSDN文档:

  

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

答案 1 :(得分:1)

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

浅层副本意味着 in 每个数组引用相同的实例。深层副本将为数组中的每个项创建新实例。

答案 2 :(得分:0)

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

答案 3 :(得分: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浅层副本

答案 4 :(得分: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"

答案 5 :(得分: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开始,但使用此类型。
  3. 完成该操作后,您现在可以更改a或b中的任何内容,而不会影响其他ArrayList中的对象。