Sự khác biệt giữa System.Array.CopyTo () và System.Array.Clone ()


82

Sự khác biệt giữa System.Array.CopyTo()và là System.Array.Clone()gì?


31
Một câu hỏi phỏng vấn ngu ngốc. "Tôi không nhớ lại, hãy để tôi kiểm tra tài liệu ..."
Cody Grey

@MisterDev Cả hai đều không giữ bất kỳ tham chiếu nào đến mảng ban đầu mà bạn đã sao chép từ đó, không.
Nyerguds

@Nyerguds Tôi nghĩ ý của anh ấy là cả hai đều giữ các tham chiếu đến các đối tượng phần tử mảng ban đầu, không phải chính đối tượng mảng ban đầu.
reirab

1
@reirab Ồ, tôi biết ý của anh ấy. Nhưng tôi thấy cần phải chỉ ra rằng anh ấy đã nói sai.
Nyerguds

Câu trả lời:


63

Phương thức Clone () trả về một đối tượng mảng mới (một bản sao cạn) chứa tất cả các phần tử trong mảng ban đầu. Phương thức CopyTo () sao chép các phần tử vào một mảng hiện có khác. Cả hai đều thực hiện một bản sao nông. Bản sao cạn có nghĩa là nội dung (mỗi phần tử mảng) chứa các tham chiếu đến cùng một đối tượng như các phần tử trong mảng ban đầu. Một bản sao sâu (mà cả hai phương thức này đều không thực hiện) sẽ tạo ra một thể hiện mới của đối tượng của mỗi phần tử, dẫn đến một đối tượng khác nhưng giống hệt nhau.

Vì vậy, sự khác biệt là:

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.
Biên tập:

Loại bỏ ví dụ sai.


6
Ví dụ của bạn là sai. Trong cái đầu tiên, numbersCopychỉ là một tham chiếu khác đến mảng được gán cho numbers. Điều này không giống như sử dụng CopyTo()phương pháp. Nếu bạn sử dụng CopyTo(), bạn sẽ nhận được kết quả tương tự như trong Clone()ví dụ của bạn . Ngoài ra, đây là C # - System.out.printlnnên được Console.WriteLine.
Graham Clark

6
Câu trả lời này, được gây hiểu lầm như khác nói là một bản sao dán từ đây: geekswithblogs.net/dforhan/archive/2005/12/01/61852.aspx
Mikhail

Theo ví dụ của GenZiy, cả hai đều là bản sao cạn. Bản sao cạn của Mảng chỉ sao chép các phần tử của Mảng, cho dù chúng là kiểu tham chiếu hay kiểu giá trị, nhưng nó không sao chép các đối tượng mà các tham chiếu tham chiếu đến. Các tham chiếu trong Mảng mới trỏ đến cùng các đối tượng mà các tham chiếu trong Mảng ban đầu trỏ tới. Ngược lại, bản sao sâu của Mảng sao chép các phần tử và mọi thứ được các phần tử tham chiếu trực tiếp hoặc gián tiếp. msdn.microsoft.com/en-us/library/system.array.clone.aspx
Mike

@PatrickDesjardins. Tôi không rõ lắm. Nếu cả hai đều là bản sao cạn Vậy thì bản sao sâu là gì. Tại sao CopyTo () là bản sao nông.
KumarHarsh

1
Trong .Net 3.5, ToArray()phương thức của Linq cung cấp một cách đơn giản hơn nhiều (và được đánh máy ) để sao chép nông một mảng. Kể từ khi Array IENumerable<T>hoạt động trên nó.
Nyerguds

27

Một sự khác biệt khác chưa được đề cập cho đến nay là

  • với Clone()mảng đích không cần tồn tại vì một mảng mới được tạo từ đầu.
  • với CopyTo()không chỉ mảng đích cần đã tồn tại mà còn cần phải đủ lớn để chứa tất cả các phần tử trong mảng nguồn từ chỉ mục bạn chỉ định làm đích.

22

Như đã nêu trong nhiều câu trả lời khác, cả hai phương thức đều thực hiện các bản sao cạn của mảng. Tuy nhiên, có những khác biệt và khuyến nghị vẫn chưa được giải quyết và được đánh dấu trong danh sách sau.

Đặc điểm của System.Array.Clone:

  • Các thử nghiệm, sử dụng .NET 4.0, cho thấy rằng nó chậm hơnCopyTo có thể là do nó sử dụng Object.MemberwiseClone;
  • Yêu cầu truyền kết quả đến kiểu thích hợp;
  • Mảng kết quả có cùng độ dài với nguồn.

Đặc điểm của System.Array.CopyTo:

  • Nhanh hơn so vớiClone khi sao chép vào mảng cùng loại;
  • Nó gọi Array.Copykế thừa là các khả năng , là những khả năng hữu ích nhất:
    • Có thể đóng hộp các phần tử kiểu giá trị thành các phần tử kiểu tham chiếu, ví dụ, sao chép một int[]mảng vào một object[];
    • Có thể mở hộp các phần tử kiểu tham chiếu thành các phần tử kiểu giá trị, ví dụ: sao chép một object[]mảng được đóng hộp intthành một int[];
    • Có thể thực hiện chuyển đổi mở rộng trên các loại giá trị, ví dụ: sao chép a int[]thành a long[].
    • Có thể downcast các phần tử, ví dụ, sao chép một Stream[]mảng vào một MemoryStream[](nếu bất kỳ phần tử nào trong mảng nguồn không thể chuyển đổi thành MemoryStreammột ngoại lệ sẽ được ném).
  • Cho phép sao chép nguồn vào một mảng đích có độ dài lớn hơn nguồn.

Cũng lưu ý, các phương thức này được tạo sẵn để hỗ trợ ICloneableICollection, vì vậy nếu bạn đang xử lý các biến của kiểu mảng, bạn không nên sử dụng Clonehoặc CopyTovà thay vào đó hãy sử dụng Array.Copyhoặc Array.ConstrainedCopy. Bản sao bị ràng buộc đảm bảo rằng nếu thao tác sao chép không thể hoàn tất thành công thì trạng thái mảng đích không bị hỏng.


Đây là thông tin chắc chắn. Vậy tại sao chúng ta không viết một phiên bản Clone chung chung, nhanh hơn? Đại loại như: Ví dụ: public static T [] ExtFastClone <T> (this T [] arr) {if (null == arr) {return null; } T [] arr2 = new T [arr.Length]; arr.CopyTo (arr2, 0); trả về arr2; } Hoặc bạn có thể làm một phiên bản đúc (để cho phép int -> dài) như: public static tout [] ExtFastClone <thiếc, tout> (điều này thiếc [] arr)
kevinarpe

Để sao chép nông cạn trên .Net 3.5 trở lên, bạn chỉ có thể sử dụng .ToArray()phương pháp của Linq . Nó vẫn tạo ra một bản sao và nó có thể được thực thi trên bất kỳ IEnumerable<>, bao gồm cả mảng. Và không giống như .Clone(), nó được đánh máy, vì vậy không cần truyền.
Nyerguds

21

Cả hai đều thực hiện các bản sao nông như @PatrickDesjardins đã nói (mặc dù nhiều linh hồn lầm lạc nghĩ rằng CopyTođó là một bản sao sâu).

Tuy nhiên, CopyTocho phép bạn sao chép một mảng vào một chỉ mục được chỉ định trong mảng đích, giúp nó linh hoạt hơn đáng kể.


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"

nguồn


1
Tôi đoán, sao chép chậm có nghĩa là chỉ các tham chiếu được sao chép, không phải giá trị. Vì vậy, nếu bạn đang thay đổi giá trị của myarray [0] từ "một" thành 0 thì giá trị của myarray1 [0] và myarray [1] cũng không được bằng 0.
Adarsh ​​Kumar

1
Xin lỗi, nhưng suy đoán của bạn là sai. Bản sao nông không phải là bản sao của các tham chiếu: "Phương thức MemberwiseClone tạo bản sao nông bằng cách tạo một đối tượng mới, rồi sao chép các trường không tĩnh của đối tượng hiện tại sang đối tượng mới." xem msdn.microsoft.com/en-us/library/…
GenZiy

1
Bản sao sâu hoặc nông không liên quan nếu các kiểu bạn đặt trong mảng của mình là nguyên thủy / bất biến . Chuỗi và số nguyên luôn tạo ra một bản sao mới khi chúng được đưa vào một thứ khác. Để kiểm tra bản sao sâu, hãy đặt một đối tượng phức tạp (như một mảng) trên một trong các điểm.
Nyerguds

2

Cả CopyTo () và Clone () đều tạo bản sao cạn. Phương thức clone () tạo bản sao của mảng ban đầu. Nó trả về một mảng độ dài chính xác.

Mặt khác, CopyTo () sao chép các phần tử từ mảng ban đầu sang mảng đích bắt đầu từ chỉ số mảng đích đã chỉ định. Lưu ý rằng, điều này thêm các phần tử vào một mảng đã tồn tại.

Đoạn mã sau sẽ mâu thuẫn với các bài đăng nói rằng CopyTo () tạo một bản sao sâu:

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

Để tôi giải thích một chút. Nếu các phần tử của mảng thuộc loại tham chiếu, thì bản sao (cả cho Clone () và CopyTo ()) sẽ được tạo ở mức đầu tiên (trên cùng). Nhưng cấp độ thấp hơn không bị sao chép. Nếu chúng ta cũng cần bản sao của cấp thấp hơn, chúng ta phải làm điều đó một cách rõ ràng. Đó là lý do tại sao sau khi Sao chép hoặc Sao chép các phần tử kiểu tham chiếu, mỗi phần tử trong mảng được Sao chép hoặc Sao chép tham chiếu đến cùng một vị trí bộ nhớ như được tham chiếu bởi phần tử tương ứng trong mảng ban đầu. Điều này chỉ ra rõ ràng rằng không có trường hợp riêng biệt nào được tạo cho cấp thấp hơn. Và nếu đúng như vậy thì việc thay đổi giá trị của bất kỳ phần tử nào trong mảng được Sao chép hoặc Nhân bản sẽ không có hiệu lực trong phần tử tương ứng của mảng ban đầu.

Tôi nghĩ rằng lời giải thích của tôi là đầy đủ nhưng tôi không tìm thấy cách nào khác để làm cho nó dễ hiểu.


1

Array.Clone()sẽ thực hiện sao chép sâu về mặt kỹ thuật, khi chuyển mảng inthoặc chuỗi tới một phương thức làm tham chiếu.

Ví dụ

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

Ngay cả khi các phương thức sắp xếp mảng số nhưng nó sẽ không ảnh hưởng đến tham chiếu thực tế được truyền đến các phương thức sắp xếp. Ví dụ mảng số sẽ có cùng định dạng ban đầu không được sắp xếp ở dòng số 1.

Lưu ý: Bản sao nên được thực hiện trong các phương pháp sắp xếp.


0

Các Clone()phương pháp không cho tham chiếu đến dụ mục tiêu chỉ cung cấp cho bạn một bản sao. cácCopyTo() phương pháp bản sao các yếu tố vào một thể hiện.

Cả hai đều không cung cấp tham chiếu của cá thể đích và như nhiều thành viên nói rằng họ cung cấp bản sao nông (bản sao ảo) mà không có tham chiếu, đây là chìa khóa.


0

Câu trả lời là khó hiểu đối với tôi. Khi bạn nói bản sao cạn, điều này có nghĩa là họ vẫn đang trỏ đến cùng một địa chỉ. Có nghĩa là, thay đổi cái này cũng sẽ thay đổi cái khác.

Vì vậy, nếu tôi có A = [1,2,3,4] và tôi sao chép nó và nhận được B = [1,2,3,4]. Bây giờ, nếu tôi thay đổi B [0] = 9. Điều này có nghĩa là A bây giờ sẽ là A = [9,2,3,4]. Đúng không?


Không. nếu chúng ta thay đổi giá trị của mảng b, nó chỉ ảnh hưởng đến mảng b đó. không phải là mảng A.
Gomathipriya

Số nguyên, chuỗi, ngày, v.v. không bao giờ bị sao chép bởi tham chiếu, mọi người . Shallow có nghĩa là "chỉ sâu một cấp". Nó có nghĩa là các kiểu tham chiếu (mảng hoặc các đối tượng phức tạp khác) sẽ vẫn trỏ đến các đối tượng giống nhau. Không phải kiểu nguyên thủy / bất biến; chúng được thiết kế để không bao giờ được sử dụng làm tài liệu tham khảo.
Nyerguds

Bản sao nông chỉ áp dụng cho các đối tượng phức tạp, chẳng hạn như cấu trúc, chuỗi, danh sách, v.v. Một mảng Int hoặc mảng kép sẽ luôn có bản sao sâu.
Zuabros

0

Cả hai đều là bản sao nông cạn. Phương thức CopyTo không phải là một bản sao sâu. Kiểm tra mã sau:

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 */

0

Array.Clone không yêu cầu một mảng đích / đích khả dụng khi gọi hàm, trong khi Array.CopyTo yêu cầu một mảng đích và một chỉ mục.


-1

Clone() được sử dụng để chỉ sao chép cấu trúc của dữ liệu / mảng, nó không sao chép dữ liệu thực tế.

CopyTo() sao chép cấu trúc cũng như dữ liệu thực tế.


-2

Xin lưu ý: Có sự khác biệt giữa việc sử dụng String [] đến StringBuilder [].

Trong Chuỗi - nếu bạn thay đổi Chuỗi, các mảng khác mà chúng tôi đã sao chép (bằng CopyTo hoặc Clone) trỏ đến cùng một chuỗi sẽ không thay đổi, nhưng mảng Chuỗi ban đầu sẽ trỏ đến một Chuỗi mới, tuy nhiên, nếu chúng ta sử dụng StringBuilder trong một mảng, con trỏ chuỗi sẽ không thay đổi, do đó, nó sẽ ảnh hưởng đến tất cả các bản sao chúng ta đã tạo cho mảng này. Ví dụ:

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
Đây không phải là liên quan đến CopyTovs Clone. Nó chỉ là ngữ nghĩa tham chiếu so với ngữ nghĩa giá trị. int là một kiểu giá trị, vì vậy bạn luôn nhận được một bản sao mới. StringBuilder có ngữ nghĩa tham chiếu, vì vậy bạn đang hành động trên cùng một bản sao.
nawfal

@nawfal - Tôi biết, đó là lý do tại sao tôi viết 'xin lưu ý' ... có sự khác biệt về hành vi giữa String, StringBuilder và int, trong copyto và clone, và nó có thể gây khó hiểu cho những người không biết về nó.
inbaly
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.