Różnica między System.Array.CopyTo () i System.Array.Clone ()

83

Jaka jest różnica między System.Array.CopyTo()i System.Array.Clone()?

Mister Dev
źródło
31
Coś w rodzaju głupiego pytania do wywiadu. „Nie pamiętam od razu, sprawdzę dokumentację…”
Cody Gray
@MisterDev Żadne z nich nie zachowa żadnego odniesienia do oryginalnej tablicy, z której skopiowałeś, nie.
Nyerguds
@Nyerguds Myślę, że miał na myśli, że oba zachowują odniesienia do oryginalnych obiektów elementów tablicy, a nie do samego oryginalnego obiektu tablicy.
reirab
1
@reirab Och, zdaję sobie sprawę, co miał na myśli. Ale czułem, że konieczne jest zwrócenie uwagi, że powiedział to źle.
Nyerguds

Odpowiedzi:

64

Metoda Clone () zwraca obiekt nowej tablicy (płytkiej kopii) zawierający wszystkie elementy oryginalnej tablicy. Metoda CopyTo () kopiuje elementy do innej istniejącej tablicy. Obaj wykonują płytką kopię. Płytka kopia oznacza, że ​​zawartość (każdy element tablicy) zawiera odniesienia do tego samego obiektu, co elementy oryginalnej tablicy. Głęboka kopia (której żadna z tych metod nie wykonuje) utworzyłaby nowe wystąpienie obiektu każdego elementu, co spowodowałoby inny, ale identyczny obiekt.

Więc różnica jest taka:

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.
Edytować:

Usuń zły przykład.

Patrick Desjardins
źródło
6
Twój przykład jest zły. W pierwszym numbersCopyjest po prostu kolejnym odwołaniem do tablicy przypisanej do numbers. To nie to samo, co użycie CopyTo()metody. Jeśli użyjesz CopyTo(), uzyskasz takie same wyniki, jak w swoim Clone()przykładzie. Również to jest C # - System.out.printlnpowinno być Console.WriteLine.
Graham Clark
6
Ta odpowiedź, która jest myląca, jak inni powiedzieli, jest kopiuj-wklej stąd: geekswithblogs.net/dforhan/archive/2005/12/01/61852.aspx
Michaił
Zgodnie z przykładem GenZiy, oba są płytką kopią. Płytka kopia Array kopiuje tylko elementy Array, niezależnie od tego, czy są to typy referencyjne, czy typy wartości, ale nie kopiuje obiektów, do których odwołują się odwołania. Odwołania w nowej tablicy wskazują na te same obiekty, na które wskazują odwołania w oryginalnej tablicy. W przeciwieństwie do tego głęboka kopia tablicy kopiuje elementy i wszystko, do czego elementy odwołują się bezpośrednio lub pośrednio. msdn.microsoft.com/en-us/library/system.array.clone.aspx
Mike
@PatrickDesjardins .Nie jest to dla mnie zbyt jasne.Jeśli oba są płytkie, to czym jest głęboka kopia.Dlaczego CopyTo () jest płytką kopią.
KumarHarsh
1
W .Net 3.5 ToArray()metoda Linq zapewnia znacznie prostszy (i wpisany typowo ) sposób płytkiego klonowania tablicy. Ponieważ Array IENumerable<T>to działa na nim.
Nyerguds
28

Jeszcze jedna różnica, o której do tej pory nie wspomniano, to to

  • z Clone()tablicą docelową nie musi jeszcze istnieć, ponieważ nowa jest tworzona od podstaw.
  • ze CopyTo()nie tylko konieczność przeznaczenia na tablicy już istnieje, to musi być wystarczająco duży, aby pomieścić wszystkie elementy w tablicy źródłowej z indeksu można określić jako miejsce docelowe.
Michael Burr
źródło
23

Jak stwierdzono w wielu innych odpowiedziach, obie metody wykonują płytkie kopie tablicy. Istnieją jednak różnice i zalecenia, które nie zostały jeszcze uwzględnione i które zostały podkreślone na poniższych listach.

Charakterystyka System.Array.Clone:

  • Testy wykorzystujące .NET 4.0 pokazują, że jest wolniejszy niżCopyTo prawdopodobnie dlatego, że używa Object.MemberwiseClone;
  • Wymaga rzutowania wyniku na odpowiedni typ;
  • Wynikowa tablica ma taką samą długość jak źródło.

Charakterystyka System.Array.CopyTo:

  • Jest szybsze niżClone przy kopiowaniu do tablicy tego samego typu;
  • Wzywa do Array.Copydziedziczenia możliwości , które są najbardziej przydatne:
    • Można umieszczać elementy typu wartościowego w elementach typu referencyjnego, na przykład kopiowanie int[]tablicy do object[];
    • Może rozpakować elementy typu referencyjnego na elementy typu wartościowego, na przykład kopiowanie object[]tablicy pudełek intdo int[];
    • Może wykonywać rozszerzające konwersje typów wartości, na przykład kopiowanie a int[]do long[].
    • Może downcast elementów, na przykład kopiowanie Stream[]tablicy do MemoryStream[](jeśli którykolwiek element w tablicy źródłowej nie jest konwertowany na MemoryStreamwyjątek).
  • Umożliwia skopiowanie źródła do tablicy docelowej, która ma długość większą niż źródło.

Należy również pamiętać, metody te są udostępniane w celu wspierania ICloneablei ICollection, więc jeśli masz do czynienia ze zmiennymi typów tablicowych nie należy używać Clonealbo CopyToi zamiast używać Array.Copylub Array.ConstrainedCopy. Ograniczona kopia zapewnia, że ​​jeśli operacja kopiowania nie może zakończyć się pomyślnie, stan tablicy docelowej nie jest uszkodzony.

João Angelo
źródło
To jest solidna informacja. Dlaczego więc nie napiszemy szybszej, ogólnej wersji Clone? Coś w rodzaju: Np .: public static T [] ExtFastClone <T> (ten T [] arr) {if (null == arr) {return null; } T [] arr2 = new T [arr.Length]; arr.CopyTo (arr2, 0); return arr2; } Lub możesz zrobić wersję castingową (aby umożliwić int -> long), taką jak: public static TOut [] ExtFastClone <TIn, TOut> (this TIn [] arr)
kevinarpe
W przypadku gołego, płytkiego klonowania na .Net 3.5 lub nowszym, możesz po prostu użyć .ToArray()metody Linq . Mimo to tworzy kopię i można ją wykonać na dowolnej IEnumerable<>, w tym na tablicach. W przeciwieństwie do .Clone()tego jest wpisany na maszynie, więc nie jest potrzebne przesyłanie.
Nyerguds
22

Obaj wykonują płytkie kopie, jak powiedział @PatrickDesjardins (pomimo wielu zwiedzionych dusz, które myślą, że CopyTorobi to głęboką kopię).

Jednak CopyToumożliwia skopiowanie jednej tablicy do określonego indeksu w tablicy docelowej, co zapewnia znacznie większą elastyczność.

FlySwat
źródło
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"

źródło

GenZiy
źródło
1
Myślę, że mała kopia oznacza, że ​​kopiowane są tylko odniesienia, a nie wartość. Więc jeśli zmieniasz wartość myarray [0] z „one” na 0, to wartość myarray1 [0] i myarray [1] również nie powinna być równa 0.
Adarsh ​​Kumar
1
Przepraszamy, ale twoje przypuszczenie jest błędne. Płytka kopia nie jest kopią odniesień: „Metoda MemberwiseClone tworzy płytką kopię, tworząc nowy obiekt, a następnie kopiując niestatyczne pola bieżącego obiektu do nowego obiektu”. patrz msdn.microsoft.com/en-us/library/…
GenZiy
1
Płytka lub głęboka kopia nie ma znaczenia, jeśli typy, które umieścisz w swojej tablicy, są prymitywne / niezmienne . Łańcuchy i liczby całkowite zawsze tworzą nową kopię, gdy zostaną umieszczone w czymś innym. Aby przetestować głęboką kopię, umieść złożony obiekt (np. Tablicę) w jednym z miejsc.
Nyerguds
2

Zarówno CopyTo (), jak i Clone () tworzą powierzchowną kopię. Metoda Clone () tworzy klon oryginalnej tablicy. Zwraca tablicę o dokładnej długości.

Z drugiej strony CopyTo () kopiuje elementy z oryginalnej tablicy do tablicy docelowej, zaczynając od określonego indeksu tablicy docelowej. Zauważ, że dodaje to elementy do już istniejącej tablicy.

Poniższy kod zaprzecza postom mówiącym, że CopyTo () tworzy głęboką kopię:

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

Pozwól, że trochę to wyjaśnię. Jeśli elementy tablicy są typu referencyjnego, kopia (zarówno dla Clone (), jak i CopyTo ()) zostanie utworzona do pierwszego (najwyższego) poziomu. Ale niższy poziom nie jest kopiowany. Jeśli potrzebujemy również kopii niższego poziomu, musimy to zrobić wyraźnie. Dlatego po klonowaniu lub kopiowaniu elementów typu referencyjnego każdy element w tablicy sklonowanej lub skopiowanej odnosi się do tej samej lokalizacji pamięci, do której odnosi się odpowiedni element w oryginalnej tablicy. To wyraźnie wskazuje, że nie jest tworzona osobna instancja dla niższego poziomu. Gdyby tak było, zmiana wartości dowolnego elementu w tablicy skopiowanej lub sklonowanej nie wpłynęłaby na odpowiedni element oryginalnej tablicy.

Myślę, że moje wyjaśnienie jest wyczerpujące, ale nie znalazłem innego sposobu, aby było zrozumiałe.

SFD
źródło
1

Array.Clone()wykonałby technicznie głęboką kopię, gdy przekazał tablicę intlub ciąg do metody jako odniesienie.

Na przykład

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

Nawet jeśli metody sortują tablicę liczb, ale nie wpłynie to na rzeczywistą referencję przekazaną do metod sortowania. Tj. Tablica liczb będzie w tym samym nieposortowanym formacie początkowym w wierszu nr 1.

Uwaga: Klon należy wykonać w metodach sortowania.

Umasankar Siva
źródło
0

Clone()Metoda nie dać odwołanie do instancji docelowej po prostu dać ci kopię. te CopyTo()metody kopiuje elementów w istniejącym przykład.

Oba nie podają odniesienia do instancji docelowej i jak wielu członków twierdzi, że podaje płytką kopię (kopię iluzji) bez odniesienia, to jest klucz.

Mohammad Ahmed
źródło
0

Odpowiedzi są dla mnie mylące. Kiedy mówisz płytka kopia, oznacza to, że nadal wskazują ten sam adres. Co oznacza, że ​​zmiana jednego z nich spowoduje również zmianę innego.

Więc jeśli mam A = [1,2,3,4] i sklonuję go i otrzymam B = [1,2,3,4]. Teraz, jeśli zmienię B [0] = 9. To oznacza, że ​​A będzie teraz A = [9,2,3,4]. Czy to jest poprawne?

Nieznajomy
źródło
Nie. jeśli zmienimy wartość tablicy b, wpłynie to tylko na tablicę b. nie tablica A.
Gomathipriya
Liczby całkowite, ciągi znaków, daty itp. Nigdy nie są kopiowane przez odniesienia, osoby . Płytki oznacza „tylko jeden poziom głębokości”. Oznacza to, że typy referencyjne (tablice lub inne złożone obiekty) nadal będą wskazywać na te same obiekty. Nie typy pierwotne / niezmienne; te są zaprojektowane tak, aby nigdy nie były używane jako odniesienia.
Nyerguds
Płytkie kopie mają zastosowanie tylko do złożonych obiektów, takich jak struktury, łańcuchy, listy itp. Tablica Int lub double zawsze będzie miała głęboką kopię.
Zuabros
0

Oba są płytkimi kopiami. CopyTo nie jest głęboką kopią. Sprawdź poniższy kod:

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 */
Nikhil Redij
źródło
0

Array.Clone nie wymaga, aby tablica docelowa / docelowa była dostępna podczas wywoływania funkcji, podczas gdy Array.CopyTo wymaga tablicy docelowej i indeksu.

Aikansh Mann
źródło
-1

Clone() służy do kopiowania tylko struktury danych / tablicy, nie kopiuje rzeczywistych danych.

CopyTo() kopiuje strukturę oraz aktualne dane.

abatishchev
źródło
-2

Uwaga: istnieje różnica między używaniem String [] do StringBuilder [].

W String - jeśli zmienisz String, inne tablice, które skopiowaliśmy (przez CopyTo lub Clone), które wskazują na ten sam ciąg, nie ulegną zmianie, ale oryginalna tablica String będzie wskazywać na nowy String, jednak jeśli użyjemy StringBuilder w tablicy wskaźnik String nie ulegnie zmianie, dlatego wpłynie to na wszystkie kopie, które utworzyliśmy dla tej tablicy. Na przykład:

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
}
niesprawiedliwy
źródło
1
To nie jest związane CopyTovs Clone. To tylko semantyka referencyjna a semantyka wartości. int jest typem wartości, więc za każdym razem otrzymujesz nową kopię. StringBuilder ma semantykę odwołań, więc działasz na tej samej kopii.
nawfal
@nawfal - wiem, dlatego napisałem „uwaga” ... istnieje różnica w zachowaniu między String, StringBuilder i int, w copyto i clone, i może to być mylące dla kogoś, kto nie jest tego świadomy.
inbaly