System.Array.CopyTo()和System.Array.Clone()之间的区别


82

System.Array.CopyTo()和之间有什么区别System.Array.Clone()


31
有点愚蠢的面试问题。“我不记得了,让我检查文档...”
Cody Gray

@MisterDev这两个都不保留对您从其复制的原始数组的任何引用,不。
Nyerguds

@Nyerguds我认为他的意思是它们都保留了对原始数组元素对象的引用,而不是对原始数组对象本身的引用。
reirab '16

1
@reirab哦,我知道他的意思。但是我觉得有必要指出他说错了。
Nyerguds

Answers:


63

克隆()方法返回一个新的数组包含原始阵列中的所有元件(浅表副本)对象。所述CopyTo从()方法复制的元素到另一个现有阵列。两者都执行浅表复制。浅表副本意味着内容(每个数组元素)包含对与原始数组中的元素相同的对象的引用。深拷贝(这些方法都不执行)将创建每个元素对象的新实例,从而产生一个不同但相同的对象。

所以区别是:

1- CopyTo require to have a destination array when Clone return a new array.
2- CopyTo let you specify an index (if required) to the destination array.
编辑:

删除错误的示例。


6
您的例子是错误的。在第一个中,numbersCopy只是对分配给的数组的另一个引用numbers。这是一样的使用CopyTo()方法。如果使用CopyTo(),您将获得与Clone()示例相同的结果。另外,这是C#-System.out.println应该是Console.WriteLine
格雷厄姆·克拉克

6
这个答案具有误导性,正如其他人所说的是从此处复制粘贴:geekswithblogs.net/dforhan/archive/2005/12/01/61852.aspx
Mikhail

根据GenZiy的示例,它们都是浅表副本。数组的浅表副本仅复制数组的元素,无论它们是引用类型还是值类型,但不复制引用所引用的对象。新Array中的引用指向与原始Array中指向的对象相同的对象。相反,数组的深层副本将复制元素以及元素直接或间接引用的所有内容。 msdn.microsoft.com/zh-CN/library/system.array.clone.aspx
Mike

@PatrickDesjardins。对我来说不是很清楚。如果两个都是浅拷贝,那么什么是深拷贝。为什么CopyTo()是浅拷贝。
KumarHarsh 2014年

1
在.net 3.5,LINQ的的ToArray()方法提供了一种简单得多的(和类型的浅克隆阵列反正的)的方式。由于数组是IENumerable<T>它的作品。
Nyerguds

27

到目前为止没有提到的另一个区别是

  • Clone()由于从头创建了一个新数组,因此带有目标数组的数组还不需要存在。
  • CopyTo()不仅目标数组必须已经存在,它需要足够大,以容纳从您指定为目标的索引源阵列中的所有元素。

22

如许多其他答案所述,这两种方法都执行数组的浅表复制。但是,存在一些差异和建议尚未解决,并在以下列表中突出显示。

特点System.Array.Clone

  • 测试中,使用.NET 4.0,表明它是慢CopyTo可能是因为它使用Object.MemberwiseClone;
  • 需要将结果转换为适当的类型;
  • 所得数组的长度与源长度相同。

特点System.Array.CopyTo

  • Clone复制到相同类型的数组时要
  • 它称呼Array.Copy继承为功能,是最有用的功能:
    • 可以将值类型元素装箱为引用类型元素,例如,将int[]数组复制到object[];中。
    • 可以将引用类型元素拆箱为值类型元素,例如,将object[]装箱的数组复制intint[]
    • 可以对值类型执行扩展转换,例如,将a复制int[]long[]
    • 可以向下转换元素,例如,将Stream[]数组复制到中MemoryStream[](如果MemoryStream抛出了源数组中的任何元素都不能转换为异常)。
  • 允许将源复制到长度大于源的目标阵列。

还要注意,这些方法可用于支持ICloneableICollection,因此,如果要处理数组类型的变量,则不应使用CloneCopyTo,而应使用Array.CopyArray.ConstrainedCopy。受限复制可确保如果复制操作无法成功完成,则目标阵列状态不会损坏。


这是可靠的信息。那么,为什么不编写更快,更通用的Clone版本呢?像这样的例子:例如:public static T [] ExtFastClone <T>(this T [] arr){if(null == arr){return null; } T [] arr2 =新的T [arr.Length]; arr.CopyTo(arr2,0); 返回arr2; }或者,您也可以执行强制转换版本(允许int-> long),例如:public static TOut [] ExtFastClone <TIn,TOut>(this TIn [] arr)
kevinarpe 2012年

对于.Net 3.5或更高版本上的裸露的浅层克隆,您可以使用Linq的.ToArray()方法。它仍然会进行复制,并且可以在任何IEnumerable<>数组(包括数组)上执行。而且.Clone(),它与键入不同,因此不需要强制转换。
Nyerguds

21

两者都执行@PatrickDesjardins所说的浅拷贝(尽管许多被误导的灵魂认为CopyTo做深拷贝)。

但是,CopyTo允许您将一个数组复制到目标数组中的指定索引,从而大大提高了灵活性。


8
object[] myarray = new object[] { "one", 2, "three", 4, "really big number", 2324573984927361 };

//create shallow copy by CopyTo
//You have to instantiate your new array first
object[] myarray2 = new object[myarray.Length];
//but then you can specify how many members of original array you would like to copy 
myarray.CopyTo(myarray2, 0);

//create shallow copy by Clone
object[] myarray1;
//here you don't need to instantiate array, 
//but all elements of the original array will be copied
myarray1 = myarray.Clone() as object[];

//if not sure that we create a shalow copy lets test it
myarray[0] = 0;
Console.WriteLine(myarray[0]);// print 0
Console.WriteLine(myarray1[0]);//print "one"
Console.WriteLine(myarray2[0]);//print "one"

来源


1
我想,浅拷贝意味着只复制引用,而不复制值。所以,如果你正在改变myArray的[0]从“一”到0的值,那么不应该myarray1 [0]和myArray的[1]的值也为0
阿达什讷库马尔

1
抱歉,您的猜测是错误的。浅表副本不是引用的副本:“ MemberwiseClone方法通过创建新对象,然后将当前对象的非静态字段复制到新对象来创建浅表副本。” 看到msdn.microsoft.com/en-us/library/...
GenZiy

1
如果放置在数组中的类型是基本/不可变的,则浅复制或深复制是无关紧要的。当字符串和整数放入其他内容时,它们始终会产生一个新副本。要测试深层副本,请在其中一个点上放置一个复杂的对象(如数组)。
Nyerguds

2

CopyTo()和Clone()都进行浅拷贝。Clone()方法复制原始数组。它返回一个精确的长度数组。

另一方面,CopyTo()从指定的目标数组索引开始,将元素从原始数组复制到目标数组。请注意,这会将元素添加到现有数组中。

下面的代码将与帖子说CopyTo()进行深拷贝相矛盾:

public class Test
{
public string s;
}

// Write Main() method and within it call test()

private void test()
{
Test[] array = new Test[1];
array[0] = new Test();
array[0].s = "ORIGINAL";

Test[] copy = new Test[1];
array.CopyTo(copy, 0);

// Next line displays "ORIGINAL"
MessageBox.Show("array[0].s = " + array[0].s);
copy[0].s = "CHANGED";

// Next line displays "CHANGED", showing that
// changing the copy also changes the original.
MessageBox.Show("array[0].s = " + array[0].s);
}

让我解释一下。如果数组的元素是引用类型,则副本(用于Clone()和CopyTo())将被复制到第一(顶层)级别。但是较低级别不会被复制。如果我们还需要较低级别的副本,则必须明确地执行它。这就是为什么在克隆或复制引用类型元素之后,克隆或复制数组中的每个元素都引用与原始数组中相应元素所引用的相同的内存位置。这清楚地表明没有为较低级别创建单独的实例。如果是这样,那么更改“复制”或“克隆”数组中任何元素的值将不会对原始数组的相应元素产生影响。

我认为我的解释是详尽无遗的,但我发现没有其他方法可以使它易于理解。


1

Array.Clone() 传递数组时,将执行技术上较深的复制 int或string给方法作为引用。

例如

int[] numbers = new int[] { -11, 12, -42, 0, 1, 90, 68, 6, -9 }; 

SortByAscending(numbers); // Sort the array in ascending order by clone the numbers array to local new array.
SortByDescending(numbers); // Same as Ascending order Clone

即使这些方法对数字数组进行了排序,但它不会影响传递给排序方法的实际引用。即,数字数组将以相同的未排序初始格式排在第1行。

注意:克隆应在排序方法中完成。


0

Clone()方法不提供对目标实例的引用,只是给您一个副本。的CopyTo()方法将元素复制到现有实例中。

两者都没有给出目标实例的引用,并且许多成员说他们给出了没有引用的浅表副本(幻觉副本),这是关键。


0

答案使我感到困惑。当您说浅拷贝时,这意味着它们仍指向相同的地址。这意味着,改变任何一个都会改变另一个。

因此,如果我有A = [1,2,3,4]并克隆它并得到B = [1,2,3,4]。现在,如果我更改B [0] =9。这意味着A现在将是A = [9,2,3,4]。那是对的吗?


没有。如果我们更改b数组的值,则仅影响该b数组。不是A数组。
Gomathipriya

整数,字符串,日期等永远不会被引用复制,人。浅表示“仅深一层”。这意味着引用类型(数组或其他复杂对象)仍将指向相同的对象。不是原始/不变类型;这些被设计成永远都不能用作参考。
Nyerguds

浅拷贝仅适用于复杂对象,例如结构,字符串,列表等。Int或double数组将始终具有深拷贝。
Zuabros

0

两者都是浅表副本。CopyTo方法不是深层副本。 检查以下代码:

public class TestClass1
{
    public string a = "test1";
}

public static void ArrayCopyClone()
{
    TestClass1 tc1 = new TestClass1();
    TestClass1 tc2 = new TestClass1();

    TestClass1[] arrtest1 = { tc1, tc2 };
    TestClass1[] arrtest2 = new TestClass1[arrtest1.Length];
    TestClass1[] arrtest3 = new TestClass1[arrtest1.Length];

    arrtest1.CopyTo(arrtest2, 0);
    arrtest3 = arrtest1.Clone() as TestClass1[];

    Console.WriteLine(arrtest1[0].a);
    Console.WriteLine(arrtest2[0].a);
    Console.WriteLine(arrtest3[0].a);

    arrtest1[0].a = "new";

    Console.WriteLine(arrtest1[0].a);
    Console.WriteLine(arrtest2[0].a);
    Console.WriteLine(arrtest3[0].a);
}

/* Output is 
test1
test1
test1
new
new
new */


-1

Clone() 用于仅复制数据/数组的结构,而不复制实际数据。

CopyTo() 复制结构以及实际数据。


-2

请注意:使用String []与StringBuilder []之间有区别。

在String中-如果更改String,则我们复制(通过CopyTo或Clone)复制的指向同一字符串的其他数组不会更改,但是原始String数组将指向新的String,但是,如果我们使用StringBuilder在数组中,字符串指针不会改变,因此,它将影响我们为此数组创建的所有副本。例如:

public void test()
{
    StringBuilder[] sArrOr = new StringBuilder[1];
    sArrOr[0] = new StringBuilder();
    sArrOr[0].Append("hello");
    StringBuilder[] sArrClone = (StringBuilder[])sArrOr.Clone();
    StringBuilder[] sArrCopyTo = new StringBuilder[1];
    sArrOr.CopyTo(sArrCopyTo,0);
    sArrOr[0].Append(" world");

    Console.WriteLine(sArrOr[0] + " " + sArrClone[0] + " " + sArrCopyTo[0]);
    //Outputs: hello world hello world hello world

    //Same result in int[] as using String[]
    int[] iArrOr = new int[2];
    iArrOr[0] = 0;
    iArrOr[1] = 1;
    int[] iArrCopyTo = new int[2];
    iArrOr.CopyTo(iArrCopyTo,0);
    int[] iArrClone = (int[])iArrOr.Clone();
    iArrOr[0]++;
    Console.WriteLine(iArrOr[0] + " " + iArrClone[0] + " " + iArrCopyTo[0]);
   // Output: 1 0 0
}

1
这与CopyTovs无关Clone。它只是参考语义与值语义。int是一种值类型,因此您每次都会获得一个新副本。StringBuilder具有引用语义,因此您要对同一副本进行操作。
nawfal 2013年

@nawfal-我知道,这就是为什么我写“请注意”的原因……在copyto和clone中,String,StringBuilder和int之间的行为有所不同,对于不了解它的人可能会感到困惑。
inbaly 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.