S. Bu cevabı neden seçeyim?
- .NET'in yapabileceği en yüksek hızı istiyorsanız bu yanıtı seçin.
- Gerçekten, gerçekten kolay bir klonlama yöntemi istiyorsanız bu cevabı göz ardı edin.
Başka bir deyişle, düzeltilmesi gereken bir performans darboğunuz yoksa ve bir profil oluşturucu ile kanıtlayamazsanız başka bir yanıtla devam edin .
Diğer yöntemlerden 10 kat daha hızlı
Derin bir klon gerçekleştirmenin aşağıdaki yöntemi:
- Serileştirme / serileştirme içeren her şeyden 10 kat daha hızlı;
- .NET'in yapabileceği teorik maksimum hıza oldukça yakın.
Ve yöntem ...
En yüksek hız için, derin bir kopya yapmak üzere Nested MemberwiseClone'u kullanabilirsiniz . Bir değer yapısını kopyalamakla neredeyse aynı hızdır ve (a) yansıma veya (b) serileştirmeden (bu sayfadaki diğer yanıtlarda açıklandığı gibi) çok daha hızlıdır.
O Not eğer kullandığınız derin kopya için İçiçe MemberwiseClone , el sınıftaki her iç içe düzeyde ve tüm basitkopyasını yöntemleri tam bir klon oluşturmak için adı geçen çağıran bir deepcopy için basitkopyasını uygulamak zorunda. Bu basit: toplamda sadece birkaç satır, aşağıdaki demo koduna bakın.
İşte 100.000 klon için göreceli performans farkını gösteren kod çıktısı:
- İç içe yapılarda İç içe Memberwise için 1.08 saniye
- İç içe sınıflarda İç içe MemberwiseClone için 4.77 saniye
- Serileştirme / Serileştirme için 39.93 saniye
Neredeyse bir yapıyı kopyalamak kadar hızlı bir sınıfta Nested MemberwiseClone kullanmak ve bir yapıyı kopyalamak, .NET'in yapabileceği teorik maksimum hıza çok yakındır.
Demo 1 of shallow and deep copy, using classes and MemberwiseClone:
Create Bob
Bob.Age=30, Bob.Purchase.Description=Lamborghini
Clone Bob >> BobsSon
Adjust BobsSon details
BobsSon.Age=2, BobsSon.Purchase.Description=Toy car
Proof of deep copy: If BobsSon is a true clone, then adjusting BobsSon details will not affect Bob:
Bob.Age=30, Bob.Purchase.Description=Lamborghini
Elapsed time: 00:00:04.7795670,30000000
Demo 2 of shallow and deep copy, using structs and value copying:
Create Bob
Bob.Age=30, Bob.Purchase.Description=Lamborghini
Clone Bob >> BobsSon
Adjust BobsSon details:
BobsSon.Age=2, BobsSon.Purchase.Description=Toy car
Proof of deep copy: If BobsSon is a true clone, then adjusting BobsSon details will not affect Bob:
Bob.Age=30, Bob.Purchase.Description=Lamborghini
Elapsed time: 00:00:01.0875454,30000000
Demo 3 of deep copy, using class and serialize/deserialize:
Elapsed time: 00:00:39.9339425,30000000
MemberwiseCopy kullanarak nasıl derin bir kopya yapılacağını anlamak için, yukarıdaki zamanları oluşturmak için kullanılan demo projesi:
// Nested MemberwiseClone example.
// Added to demo how to deep copy a reference class.
[Serializable] // Not required if using MemberwiseClone, only used for speed comparison using serialization.
public class Person
{
public Person(int age, string description)
{
this.Age = age;
this.Purchase.Description = description;
}
[Serializable] // Not required if using MemberwiseClone
public class PurchaseType
{
public string Description;
public PurchaseType ShallowCopy()
{
return (PurchaseType)this.MemberwiseClone();
}
}
public PurchaseType Purchase = new PurchaseType();
public int Age;
// Add this if using nested MemberwiseClone.
// This is a class, which is a reference type, so cloning is more difficult.
public Person ShallowCopy()
{
return (Person)this.MemberwiseClone();
}
// Add this if using nested MemberwiseClone.
// This is a class, which is a reference type, so cloning is more difficult.
public Person DeepCopy()
{
// Clone the root ...
Person other = (Person) this.MemberwiseClone();
// ... then clone the nested class.
other.Purchase = this.Purchase.ShallowCopy();
return other;
}
}
// Added to demo how to copy a value struct (this is easy - a deep copy happens by default)
public struct PersonStruct
{
public PersonStruct(int age, string description)
{
this.Age = age;
this.Purchase.Description = description;
}
public struct PurchaseType
{
public string Description;
}
public PurchaseType Purchase;
public int Age;
// This is a struct, which is a value type, so everything is a clone by default.
public PersonStruct ShallowCopy()
{
return (PersonStruct)this;
}
// This is a struct, which is a value type, so everything is a clone by default.
public PersonStruct DeepCopy()
{
return (PersonStruct)this;
}
}
// Added only for a speed comparison.
public class MyDeepCopy
{
public static T DeepCopy<T>(T obj)
{
object result = null;
using (var ms = new MemoryStream())
{
var formatter = new BinaryFormatter();
formatter.Serialize(ms, obj);
ms.Position = 0;
result = (T)formatter.Deserialize(ms);
ms.Close();
}
return (T)result;
}
}
Ardından demoyu ana hattan arayın:
void MyMain(string[] args)
{
{
Console.Write("Demo 1 of shallow and deep copy, using classes and MemberwiseCopy:\n");
var Bob = new Person(30, "Lamborghini");
Console.Write(" Create Bob\n");
Console.Write(" Bob.Age={0}, Bob.Purchase.Description={1}\n", Bob.Age, Bob.Purchase.Description);
Console.Write(" Clone Bob >> BobsSon\n");
var BobsSon = Bob.DeepCopy();
Console.Write(" Adjust BobsSon details\n");
BobsSon.Age = 2;
BobsSon.Purchase.Description = "Toy car";
Console.Write(" BobsSon.Age={0}, BobsSon.Purchase.Description={1}\n", BobsSon.Age, BobsSon.Purchase.Description);
Console.Write(" Proof of deep copy: If BobsSon is a true clone, then adjusting BobsSon details will not affect Bob:\n");
Console.Write(" Bob.Age={0}, Bob.Purchase.Description={1}\n", Bob.Age, Bob.Purchase.Description);
Debug.Assert(Bob.Age == 30);
Debug.Assert(Bob.Purchase.Description == "Lamborghini");
var sw = new Stopwatch();
sw.Start();
int total = 0;
for (int i = 0; i < 100000; i++)
{
var n = Bob.DeepCopy();
total += n.Age;
}
Console.Write(" Elapsed time: {0},{1}\n\n", sw.Elapsed, total);
}
{
Console.Write("Demo 2 of shallow and deep copy, using structs:\n");
var Bob = new PersonStruct(30, "Lamborghini");
Console.Write(" Create Bob\n");
Console.Write(" Bob.Age={0}, Bob.Purchase.Description={1}\n", Bob.Age, Bob.Purchase.Description);
Console.Write(" Clone Bob >> BobsSon\n");
var BobsSon = Bob.DeepCopy();
Console.Write(" Adjust BobsSon details:\n");
BobsSon.Age = 2;
BobsSon.Purchase.Description = "Toy car";
Console.Write(" BobsSon.Age={0}, BobsSon.Purchase.Description={1}\n", BobsSon.Age, BobsSon.Purchase.Description);
Console.Write(" Proof of deep copy: If BobsSon is a true clone, then adjusting BobsSon details will not affect Bob:\n");
Console.Write(" Bob.Age={0}, Bob.Purchase.Description={1}\n", Bob.Age, Bob.Purchase.Description);
Debug.Assert(Bob.Age == 30);
Debug.Assert(Bob.Purchase.Description == "Lamborghini");
var sw = new Stopwatch();
sw.Start();
int total = 0;
for (int i = 0; i < 100000; i++)
{
var n = Bob.DeepCopy();
total += n.Age;
}
Console.Write(" Elapsed time: {0},{1}\n\n", sw.Elapsed, total);
}
{
Console.Write("Demo 3 of deep copy, using class and serialize/deserialize:\n");
int total = 0;
var sw = new Stopwatch();
sw.Start();
var Bob = new Person(30, "Lamborghini");
for (int i = 0; i < 100000; i++)
{
var BobsSon = MyDeepCopy.DeepCopy<Person>(Bob);
total += BobsSon.Age;
}
Console.Write(" Elapsed time: {0},{1}\n", sw.Elapsed, total);
}
Console.ReadKey();
}
Yine, bu not eğer kullandığınız derin kopya için İçiçe MemberwiseClone , sahip manuel sınıftaki her iç içe düzeyde ve tüm basitkopyasını yöntemleri tam bir klon oluşturmak için adı geçen çağıran bir deepcopy için basitkopyasını uygulamaktır. Bu basit: toplamda sadece birkaç satır, yukarıdaki demo koduna bakın.
Değer türleri ve Referans Türleri
Bir nesneyi klonlama söz konusu olduğunda, bir " yapı " ve " sınıf " arasında büyük bir fark olduğunu unutmayın :
- Bir " yapınız " varsa, bu sadece kopyalayabilmeniz için bir değer türüdür ve içerik klonlanır (ancak bu yazıdaki teknikleri kullanmadığınız sürece sadece sığ bir klon yapar).
- Bir " sınıfınız " varsa, bu bir referans türüdür , bu yüzden kopyalarsanız, yaptığınız tek şey işaretçiyi buna kopyalamaktır. Gerçek bir klon oluşturmak için, daha yaratıcı olmanız ve bellekte orijinal nesnenin başka bir kopyasını oluşturan değer türleri ve başvuru türleri arasındaki farkları kullanmanız gerekir .
Değer türleri ve referans türleri arasındaki farkları görün .
Hata ayıklamaya yardımcı olacak sağlama toplamları
- Nesneleri yanlış klonlamak, sabitlenmesi çok zor olan hatalara yol açabilir. Üretim kodunda, nesnenin düzgün bir şekilde klonlandığını ve başka bir referansla bozulmadığını iki kez kontrol etmek için bir sağlama toplamı uygulama eğilimindeyim. Bu sağlama toplamı Serbest Bırakma modunda kapatılabilir.
- Bu yöntemi oldukça kullanışlı buluyorum: genellikle, tüm nesnenin değil, yalnızca nesnenin parçalarını klonlamak istiyorsunuz.
Diğer birçok iplikten birçok ipliğin ayrıştırılması için gerçekten yararlıdır
Bu kod için mükemmel bir kullanım örneği, üretici / tüketici modelini uygulamak için iç içe bir sınıfın veya yapının klonlarını bir kuyruğa beslemektir.
- Sahip oldukları bir sınıfı değiştiren bir (veya daha fazla) iş parçacığımız olabilir ve bu sınıfın tam bir kopyasını bir
ConcurrentQueue
.
- Daha sonra bu sınıfların kopyalarını çekip onlarla ilgilenen bir (veya daha fazla) iş parçacığımız var.
Bu pratikte son derece iyi çalışır ve birçok iş parçacığını (üretici) bir veya daha fazla iş parçacığından (tüketiciler) ayırmamızı sağlar.
Ve bu yöntem de körü körüne hızlıdır: İç içe geçmiş yapılar kullanırsak, iç içe geçmiş sınıfları serileştirmek / serileştirmekten 35 kat daha hızlıdır ve makinede bulunan tüm iş parçacıklarından yararlanmamızı sağlar.
Güncelleme
Görünüşe göre, ExpressMapper yukarıdaki gibi el kodlamasından daha hızlı olmasa da daha hızlıdır. Bir profilerle nasıl karşılaştırdıklarını görmek zorunda kalabilirim.