Q. 왜이 답변을 선택해야합니까?
- 가장 빠른 속도의 .NET이 가능한 경우이 답변을 선택하십시오.
- 정말로, 정말로 쉬운 복제 방법을 원한다면이 답변을 무시하십시오.
다시 말해, 수정이 필요한 성능 병목 현상이 없으면 다른 답변을 사용하여 프로파일 러로이를 입증 할 수 있습니다 .
다른 방법보다 10 배 빠름
딥 클론을 수행하는 다음 방법은 다음과 같습니다.
- 직렬화 / 역 직렬화와 관련된 것보다 10 배 빠릅니다.
- 이론상 최대 속도 인 .NET과 거의 비슷합니다.
그리고 방법은 ...
최고의 속도를 위해 Nested MemberwiseClone을 사용 하여 딥 카피를 수행 할 수 있습니다 . 값 구조체를 복사하는 것과 거의 같은 속도이며 (a) 리플렉션 또는 (b) 직렬화 (이 페이지의 다른 답변에서 설명)보다 훨씬 빠릅니다.
깊은 복사에 Nested MemberwiseClone을 사용 하는 경우 클래스의 각 중첩 수준에 대해 ShallowCopy를 수동으로 구현해야하며 DeepCopy는 모든 ShallowCopy 메소드를 호출하여 완전한 복제본을 작성해야합니다. 이것은 간단합니다. 총 몇 줄 밖에되지 않습니다. 아래 데모 코드를 참조하십시오.
100,000 개의 클론에 대한 상대적인 성능 차이를 보여주는 코드 출력은 다음과 같습니다.
- 중첩 된 구조체에서 Nested MemberwiseClone의 경우 1.08 초
- 중첩 클래스에서 중첩 MemberwiseClone의 경우 4.77 초
- 직렬화 / 직렬화 해제시 39.93 초
구조체를 복사하는 것만 큼 빠른 클래스에서 Nested MemberwiseClone을 사용하고 구조체를 복사하는 것은 .NET의 이론상 최대 속도와 거의 비슷합니다.
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를 사용하여 딥 카피를 수행하는 방법을 이해하려면 위의 시간을 생성하는 데 사용 된 데모 프로젝트가 있습니다.
// 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;
}
}
그런 다음 main에서 데모를 호출하십시오.
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();
}
다시 깊은 복사에 Nested MemberwiseClone을 사용 하는 경우 클래스의 각 중첩 수준에 대해 ShallowCopy를 수동으로 구현해야하며 DeepCopy는 모든 ShallowCopy 메소드를 호출하여 완전한 복제본을 작성해야합니다. 이것은 간단합니다. 총 몇 줄 밖에되지 않습니다. 위의 데모 코드를 참조하십시오.
값 유형과 참조 유형
객체 복제 와 관련하여 " struct "와 " class " 사이에는 큰 차이가 있습니다 .
- " struct " 가있는 경우 값 유형 이므로 복사 만하면 내용이 복제됩니다 (그러나이 게시물의 기술을 사용하지 않는 한 얕은 복제본 만 생성 함).
- " 클래스 " 가있는 경우 참조 유형 이므로 복사하면 포인터를 복사하기 만하면됩니다. 실제 복제본을 만들려면보다 창의적이어야 하고 메모리에 원래 객체의 다른 사본을 생성하는 값 유형과 참조 유형의 차이를 사용해야 합니다.
값 유형과 참조 유형의 차이점을 참조하십시오 .
디버깅을 돕는 체크섬
- 객체를 잘못 복제하면 매우 찾기 어려운 버그가 발생할 수 있습니다. 프로덕션 코드에서는 객체가 올바르게 복제되었으며 다른 참조로 손상되지 않았는지 확인하는 체크섬을 구현하는 경향이 있습니다. 이 체크섬은 해제 모드에서 끌 수 있습니다.
- 이 방법이 매우 유용하다는 것을 알았습니다. 종종 전체가 아닌 객체의 일부만 복제하려고합니다.
많은 다른 스레드에서 많은 스레드를 분리하는 데 매우 유용합니다.
이 코드의 훌륭한 사용 사례 중 하나는 중첩 클래스 또는 구조체의 복제본을 큐에 공급하여 생산자 / 소비자 패턴을 구현하는 것입니다.
- 하나 이상의 스레드가 소유 한 클래스를 수정 한 다음이 클래스의 전체 사본을
ConcurrentQueue
있습니다.
- 그런 다음 하나 이상의 스레드가 이러한 클래스의 복사본을 꺼내어 처리합니다.
이것은 실제로 매우 잘 작동하며 하나 이상의 스레드 (소비자)에서 많은 스레드 (생산자)를 분리 할 수 있습니다.
중첩 된 구조체를 사용하면 중첩 된 클래스를 직렬화 / 역 직렬화하는 것보다 35 배 빠르며 머신에서 사용 가능한 모든 스레드를 활용할 수 있습니다.
최신 정보
분명히 ExpressMapper는 위와 같은 수동 코딩보다 빠르지는 않지만 빠릅니다. 그들이 프로파일 러와 어떻게 비교되는지보아야 할 수도 있습니다.