System.Array.CopyTo () ve System.Array.Clone () arasındaki fark


83

Arasındaki fark nedir System.Array.CopyTo()ve System.Array.Clone()?


31
Aptalca bir röportaj sorusu. "Anımsamıyorum, belgeleri kontrol edeyim ..."
Cody Gray

@MisterDev Bunların hiçbiri kopyaladığınız orijinal diziye herhangi bir referans tutmaz, hayır.
Nyerguds

@Nyerguds Sanırım her ikisinin de orijinal dizi nesnesine değil, orijinal dizi öğesi nesnelerine referansları tuttuklarını kastetti.
reirab

1
@reirab Oh, ne demek istediğinin farkındayım. Ama yanlış söylediğini belirtmek zorunda hissettim.
Nyerguds

Yanıtlar:


64

Klon () metodu yeni bir dizi orijinal dizideki tüm elemanları ihtiva eden (basit bir kopyasını) nesnesini geri gönderir. CopyTo () metodu kopya olan başka bir diziye elemanları. Her ikisi de sığ bir kopya oluşturur. Yüzeysel kopya, içeriklerin (her dizi öğesi) orijinal dizideki öğelerle aynı nesneye referanslar içerdiği anlamına gelir. Derin bir kopya (bu yöntemlerden hiçbirinin gerçekleştirmediği), her bir öğenin nesnesinin yeni bir örneğini oluşturarak farklı, ancak aynı bir nesne ortaya çıkarır.

Yani fark:

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.
Düzenle:

Yanlış örneği kaldırın.


6
Örneğiniz yanlış. İlki, numbersCopyatanan diziye başka bir referanstır numbers. Bu, yöntemi kullanmakla aynı şey değildirCopyTo() . Eğer kullanırsanız CopyTo(), Clone()örneğinizdekiyle aynı sonuçları alırsınız . Ayrıca, bu C # - System.out.printlnolmalıdır Console.WriteLine.
Graham Clark

6
Diğer söylediği gibi yanıltıcı olan bu cevap, buradan bir kopyalayıp yapıştırmadır: geekswithblogs.net/dforhan/archive/2005/12/01/61852.aspx
Mikhail

GenZiy'nin örneğine göre, her ikisi de sığ kopya. Bir Dizinin basit bir kopyası, ister başvuru türü ister değer türü olsun, yalnızca Dizinin öğelerini kopyalar, ancak referansların başvurduğu nesneleri kopyalamaz. Yeni Dizideki referanslar, orijinal Dizideki referansların işaret ettiği aynı nesnelere işaret eder. Buna karşılık, bir Dizinin derin bir kopyası, öğeleri ve öğeler tarafından doğrudan veya dolaylı olarak referans verilen her şeyi kopyalar. msdn.microsoft.com/en-us/library/system.array.clone.aspx
Mike

@PatrickDesjardins .Benim için çok açık değil.Eğer ikisi de sığ kopya ise o zaman derin kopya nedir.
KumarHarsh

1
.Net 3.5'te, Linq'in ToArray()yöntemi zaten bir diziyi sığ klonlamanın çok daha basit (ve tiplenmiş ) bir yolunu sağlar. Dizi olduğu IENumerable<T>için üzerinde çalışıyor.
Nyerguds

28

Şimdiye kadar belirtilmeyen bir diğer fark ise

  • ile Clone()bir yenisi sıfırdan oluşturulur beri hedef dizisi ihtiyacı yok henüz var.
  • ile CopyTo()zaten hedef dizisi ihtiyacı ortadan yok kalmaz aynı hedef olarak belirtmek dizinden kaynak dizideki tüm elemanları tutmaya yeterli büyüklükte olması gerekir.

23

Diğer birçok cevapta belirtildiği gibi, her iki yöntem de dizinin yüzeysel kopyalarını gerçekleştirir . Ancak, henüz ele alınmamış ve aşağıdaki listelerde vurgulanan farklılıklar ve öneriler vardır.

Özellikleri System.Array.Clone:

  • .NET 4.0 kullanan testler, kullandığı için muhtemelen olduğundan daha yavaş olduğunuCopyTo göstermektedir Object.MemberwiseClone;
  • Sonucun uygun türe dönüştürülmesini gerektirir ;
  • Elde edilen dizi kaynakla aynı uzunluğa sahiptir.

Özellikleri System.Array.CopyTo:

  • CloneAynı türden diziye kopyalamadan daha hızlıdır ;
  • Array.CopyEn kullanışlı olan yeteneklerden miras almaya çağırır :
    • Değer türü öğelerini, örneğin bir int[]diziyi bir object[];
    • Referans türü öğelerin kutusunu değer türü öğelerine ayırabilir, örneğin, bir object[]kutulu diziyi intbir int[];
    • Değer türlerinde genişletme dönüşümleri gerçekleştirebilir, örneğin, a'yı int[]bir long[].
    • Aşağıya aktarılabilir öğeler, örneğin, bir Stream[]diziyi a'ya kopyalamak MemoryStream[](kaynak dizideki herhangi bir öğe MemoryStreambir istisnaya dönüştürülemiyorsa ).
  • Kaynağın, kaynaktan daha büyük bir uzunluğa sahip bir hedef diziye kopyalanmasına izin verir.

Ayrıca, bu yöntemlerin desteklenmesi için sağlandığını ICloneableve ICollectionbu nedenle dizi türlerinin değişkenleriyle uğraşıyorsanız, kullanmamalısınız Cloneveya CopyTobunun yerine Array.Copyveya kullanmalısınız Array.ConstrainedCopy. Kısıtlı kopya, kopyalama işlemi başarılı bir şekilde tamamlanamazsa hedef dizi durumunun bozulmamasını sağlar.


Bu sağlam bir bilgidir. Öyleyse neden Clone'un daha hızlı, genel bir versiyonunu yazmıyoruz? Şuna benzer bir şey: Örn: public static T [] ExtFastClone <T> (bu T [] arr) {if (null == arr) {return null; } T [] dizi2 = yeni T [dizi uzunluğu]; arr.CopyTo (dizi2, 0); dönüş arr2; } Ya da bir çevrim sürümü (int -> long'a izin vermek için) şöyle yapabilirsiniz: public static TOut [] ExtFastClone <TIn, TOut> (bu TIn [] arr)
kevinarpe

.Net 3.5 veya daha yüksek sürümlerde çıplak sığ klonlama için, sadece Linq .ToArray()yöntemini kullanabilirsiniz . Yine de bir kopya oluşturur ve IEnumerable<>diziler dahil herhangi bir yerde çalıştırılabilir . Ve aksine .Clone(), yazılmıştır, bu nedenle döküm gerekmez.
Nyerguds

22

Her ikisi de @ PatrickDesjardins'in dediği gibi sığ kopyalar yapıyor (bunun CopyToderin bir kopya olduğunu düşünen birçok yanlış yönlendirilmiş ruha rağmen ).

Ancak, CopyTobir diziyi hedef dizideki belirli bir dizine kopyalamanıza izin vererek, ona önemli ölçüde daha fazla esneklik sağlar.


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"

kaynak


1
Sanırım, kopyala sadece referansların kopyalandığı anlamına gelir, değerin değil. Öyleyse, myarray [0] değerini "bir" den 0'a değiştiriyorsanız, myarray1 [0] ve myarray [1] değerlerinin de 0 olmamalıdır.
Adarsh ​​Kumar

1
Üzgünüm ama tahmininiz yanlış. Yüzeysel kopya, referansların bir kopyası değildir: "MemberwiseClone yöntemi, yeni bir nesne oluşturarak ve ardından mevcut nesnenin statik olmayan alanlarını yeni nesneye kopyalayarak basit bir kopya oluşturur." bkz. msdn.microsoft.com/en-us/library/…
GenZiy

1
Dizinize koyduğunuz türler ilkel / değişmez ise, sığ veya derin kopya önemsizdir . Dizeler ve tamsayılar başka bir şeye yerleştirildiklerinde her zaman yeni bir kopya üretirler. Derin kopyayı test etmek için noktalardan birine karmaşık bir nesne (dizi gibi) yerleştirin.
Nyerguds

2

Hem CopyTo () hem de Clone () sığ kopya oluşturur. Clone () yöntemi, orijinal dizinin bir klonunu oluşturur. Tam uzunlukta bir dizi döndürür.

Öte yandan, CopyTo (), öğeleri orijinal diziden, belirtilen hedef dizi dizininden başlayarak hedef diziye kopyalar. Bunun zaten var olan bir diziye eleman eklediğini unutmayın.

Aşağıdaki kod, CopyTo () öğesinin derin bir kopya oluşturduğunu söyleyen kayıtlarla çelişecektir:

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

Biraz açıklayayım. Dizinin öğeleri başvuru türlerindeyse, kopya (hem Clone () hem de CopyTo () için) birinci (üst) seviyeye kadar oluşturulacaktır. Ancak alt düzey kopyalanmaz. Alt seviyenin kopyasına da ihtiyacımız varsa, bunu açıkça yapmalıyız. Referans tipi elemanların Klonlanmasından veya Kopyalanmasından sonra, Cloned veya Copied dizisindeki her eleman, orijinal dizideki karşılık gelen eleman tarafından atıfta bulunulan aynı bellek konumuna başvurur. Bu, daha düşük seviye için ayrı bir örneğin oluşturulmadığını açıkça gösterir. Ve eğer öyleyse, Kopyalanmış veya Klonlanmış dizideki herhangi bir öğenin değerini değiştirmek, orijinal dizinin karşılık gelen öğesi üzerinde etkili olmayacaktır.

Açıklamamın kapsamlı olduğunu düşünüyorum, ancak bunu anlaşılır kılmanın başka bir yolunu bulamadım.


1

Array.Clone()dizi intveya dizeyi referans olarak bir yönteme iletirken teknik olarak derin kopyalama gerçekleştirir .

Örneğin

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

Yöntemler sayı dizisini sıralasa, ancak sıralama yöntemlerine iletilen gerçek referansı etkilemeyecek olsa bile, sayı dizisi 1 numaralı satırda aynı sıralanmamış başlangıç ​​biçiminde olacaktır.

Not: Klonlama, sıralama yöntemlerinde yapılmalıdır.


0

Clone()Yöntem sadece size bir kopyasını vermek hedef örneğine referans vermezler. CopyTo()yöntem kopyalar mevcut bir örneğine elemanları.

Her ikisi de hedef örneğin referansını vermez ve birçok üye referans olmadan sığ kopya (illüzyon kopyası) verdiklerini söylediği için anahtar budur.


0

Cevaplar kafamı karıştırıyor. Yüzeysel kopya dediğinizde, bu onların hala aynı adresi gösterdikleri anlamına gelir. Bu, birini değiştirmenin diğerini de değiştireceği anlamına gelir.

Yani eğer A = [1,2,3,4] varsa ve onu klonlarsam ve B = [1,2,3,4] elde edersem. Şimdi, eğer B [0] = 9'u değiştirirsem. Bu, A'nın artık A = [9,2,3,4] olacağı anlamına gelir. Bu doğru mu?


Hayır. b dizisinin değerini değiştirirsek, yalnızca o b dizisini etkiler. A dizisi değil.
Gomathipriya

Tam sayılar, dizeler, tarihler vb. Asla referans, insanlar tarafından kopyalanmaz . Sığ, "yalnızca bir seviye derin" anlamına gelir. Bu, referans türlerinin (diziler veya diğer karmaşık nesneler) yine de aynı nesneleri göstereceği anlamına gelir . İlkel / değişmez tipler değil; bunlar asla referans olarak kullanılmayacak şekilde tasarlanmıştır.
Nyerguds

Yüzeysel kopyalar yalnızca yapılar, dizeler, listeler vb. Gibi karmaşık nesnelere uygulanır. Bir Int veya double dizinin her zaman derin bir kopyası olacaktır.
Zuabros

0

Her ikisi de sığ kopyalar. CopyTo yöntemi derin bir kopya değildir. Aşağıdaki kodu kontrol edin:

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 , işlev çağrılırken bir hedef / hedef dizisinin kullanılabilir olmasını gerektirmezken , Array.CopyTo bir hedef dizi ve bir dizin gerektirir.


-1

Clone() yalnızca veri / dizi yapısını kopyalamak için kullanılır, gerçek verileri kopyalamaz.

CopyTo() yapıyı ve gerçek verileri kopyalar.


-2

Lütfen dikkat: String [] kullanımı ile StringBuilder [] arasında bir fark vardır.

String'de - String'i değiştirirseniz, kopyaladığımız ve aynı dizeyi gösteren diğer diziler (CopyTo veya Clone ile) değişmeyecek, ancak orijinal String dizisi yeni bir String'i gösterecek, ancak bir StringBuilder kullanırsak bir dizide, String işaretçisi değişmeyecek, bu nedenle bu dizi için yaptığımız tüm kopyaları etkileyecektir. Örneğin:

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
Bu CopyTovs ile ilgili değildir Clone. Bu sadece referans anlambilim ve değer anlambilimidir. int bir değer türüdür, bu nedenle her seferinde yeni bir kopya alırsınız. StringBuilder'ın referans semantiği vardır, bu nedenle aynı kopya üzerinde hareket ediyorsunuz.
nawfal

@nawfal - Biliyorum, bu yüzden 'lütfen not et' yazdım ... String, StringBuilder ve int arasında copyto ve clone'da bir davranış farkı var ve bunun farkında olmayan biri için kafa karıştırıcı olabilir.
inbaly
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.