Різниця між System.Array.CopyTo () та System.Array.Clone ()


83

Яка різниця між System.Array.CopyTo()і System.Array.Clone()?


31
Німе запитання про інтерв’ю. "Я не згадую випадково, дозвольте мені перевірити документацію ..."
Коді Грей

@MisterDev Жоден з них не зберігатиме жодного посилання на оригінальний масив, з якого ви скопіювали, ні.
Nyerguds

@Nyerguds Я думаю, він мав на увазі, що вони обидва зберігають посилання на оригінальні об'єкти масиву, а не на сам об'єкт оригінального масиву.
reirab

1
@reirab О, я знаю, що він мав на увазі. Але я вважав за необхідне зазначити, що він сказав це неправильно.
Nyerguds

Відповіді:


64

Метод Clone () повертає новий об'єкт масиву (неглибока копія), що містить усі елементи вихідного масиву. Метод 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
Михайло

За прикладом GenZiy, обидва вони є неглибокою копією. Неглибока копія масиву копіює лише елементи масиву, незалежно від того, є вони посилальними типами чи типами значень, але вона не копіює об’єкти, на які посилаються посилання. Посилання в новому масиві вказують на ті самі об'єкти, на які вказують посилання в оригінальному масиві. На відміну від них, глибока копія масиву копіює елементи та все, на що елементи прямо чи опосередковано посилаються. msdn.microsoft.com/en-us/library/system.array.clone.aspx
Майк

@PatrickDesjardins. Мені це не дуже зрозуміло. Якщо обидва - неглибока копія, то що таке глибока копія. Чому CopyTo () - неглибока копія.
KumarHarsh

1
У .Net 3.5 ToArray()метод Linq надає набагато простіший (і набраний ) спосіб дрібного клонування масиву в будь-якому випадку. Оскільки Array IENumerable<T>це працює на ньому.
Nyerguds

28

Ще одна відмінність, про яку до цього часу не згадується, полягає в тому,

  • з Clone()цільовим масивом ще не потрібно існувати, оскільки новий створюється з нуля.
  • з CopyTo()не тільки необхідність масив призначення вже існує, він повинен бути досить великим , щоб вмістити всі елементи в вихідному масиві з індексу ви вказуєте в якості пункту призначення.

23

Як зазначено в багатьох інших відповідях, обидва методи виконують неглибокі копії масиву. Однак існують розбіжності та рекомендації, які ще не були розглянуті і які виділено в наступних списках.

Характеристики System.Array.Clone:

  • Тести, що використовують .NET 4.0, показують, що він працює повільніше, ніж,CopyTo можливо, тому, що він використовує Object.MemberwiseClone;
  • Потрібно відлити результат до відповідного типу;
  • Отриманий масив має однакову довжину з вихідним.

Характеристики System.Array.CopyTo:

  • Швидше, ніжClone при копіюванні в масив того ж типу;
  • Це вимагає Array.Copyуспадкування можливостей , які є найбільш корисними:
    • Може вміщувати елементи типу значення в елементи довідкового типу, наприклад, копіюючи int[]масив у файл object[];
    • Може розпаковувати елементи посилання типу в елементи типу значення, наприклад, копіюючи object[]масив, введений intу поле int[];
    • Може виконувати розширення перетворень для типів значень, наприклад, копіюючи a int[]в a long[].
    • Може знижувати елементи, наприклад, копіюючи Stream[]масив в MemoryStream[](якщо будь-який елемент у вихідному масиві не конвертований у MemoryStreamвиняток).
  • Дозволяє скопіювати джерело в цільовий масив, довжина якого перевищує джерело.

Також зверніть увагу, що ці методи доступні для підтримки ICloneableі ICollection, таким чином , якщо ви маєте справу зі змінними типів масивів , ви не повинні використовувати Cloneабо CopyToі натомість використовувати Array.Copyабо Array.ConstrainedCopy. Обмежена копія гарантує, що якщо операція копіювання не може завершитися успішно, стан цільового масиву не пошкоджений.


Це достовірна інформація. То чому б нам не написати швидшу, загальну версію Clone? Щось на зразок: Приклад: загальнодоступний статичний T [] ExtFastClone <T> (цей T [] обр.) {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

Для оголеного неглибокого клонування на .Net 3.5 або новішої версії ви можете просто скористатися .ToArray()методом Лінка . Він робить копію в будь-якому випадку і може бути виконаний на будь-якому IEnumerable<>, включаючи масиви. І на відміну від цього .Clone(), він набраний, тому кастинг не потрібен.
Nyerguds

22

Обидва виконують неглибокі копії, як сказав @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
Думаю, shalow copy означає, що копіюються лише посилання, а не значення. Отже, якщо ви змінюєте значення myarray [0] з "one" на 0, тоді значення myarray1 [0] і myarray [1] також не повинно бути 0.
Adarsh ​​Kumar

1
Вибачте, але ваша здогадка помилкова. Дрібна копія не є копією посилань: "Метод MemberwiseClone створює дрібну копію, створюючи новий об'єкт, а потім копіюючи нестатичні поля поточного об'єкта в новий об'єкт." див. msdn.microsoft.com/en-us/library/…
GenZiy 25.03.14

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або рядок методу як посилання.

Наприклад

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.
Гоматипрія

Цілі числа, рядки, дати тощо не копіюються за посиланням, люди . Дрібний означає "глибина лише на один рівень". Це означає, що посилальні типи (масиви чи інші складні об'єкти) все одно вказуватимуть на ті самі об'єкти. Не примітивні / незмінні типи; вони розроблені для того, щоб ніколи не використовувати їх як посилання.
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 */

0

Array.Clone не вимагає наявності цільового / цільового масиву під час виклику функції, тоді як Array.CopyTo вимагає масиву призначення та індексу.


-1

Clone() використовується для копіювання лише структури даних / масиву, він не копіює фактичні дані.

CopyTo() копіює структуру, а також фактичні дані.


-2

Зверніть увагу: Існує різниця між використанням String [] та StringBuilder [].

У рядку - якщо ви зміните рядок, інші скопійовані нами масиви (за допомогою CopyTo або Clone), що вказує на ту саму рядок, не зміняться, але оригінальний масив String вкаже на новий рядок, однак, якщо ми використовуємо StringBuilder в масиві покажчик String не зміниться, отже, це вплине на всі копії, які ми зробили для цього масиву. Наприклад:

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
Це не пов'язано з CopyToпроти Clone. Це просто посилальна семантика проти семантики значення. int - це тип значення, тому ви щоразу отримуєте нову копію. StringBuilder має посилальну семантику, тому ви дієте на одній копії.
nawfal

@nawfal - Я знаю, саме тому я написав "будь ласка, зверніть увагу" ... існує різниця у поведінці між String, StringBuilder та int, в copyto та clone, і це може заплутати когось, хто цього не знає.
inbaly
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.