어떤 경우에는 개체를 채우기 위해 데이터베이스에서 데이터를 읽어야 하거나 개체를 채우기 위해 하드 디스크에서 파일을 읽어야 하지만 이는 상대적으로 시간이 많이 걸립니다. 이때 우리는 객체 복사를 생각합니다. 이 글에서는 C#의 얕은 복사(shallow copy)와 깊은 복사(deep copy)의 사용법을 예제 형식으로 분석합니다.
1. 얕은 복사
1. "얕은 복사"란:
객체를 미리 복사할 때, 개체 자체를 복사합니다. 개체의 참조 유형 멤버의 경우 개체 참조만 복사합니다. 이 참조는 관리되는 힙의 개체 인스턴스를 가리킵니다.
2. 참조 유형의 클래스 멤버와 값 유형의 구조체 멤버를 포함하는 개체가 있습니다.
Cinema에는 참조 유형 멤버 Room과 값 유형 멤버 Film이 포함되어 있습니다.
public class Room { public int _maxSeat; public Room(int maxSeat) { this._maxSeat = maxSeat; } } public struct Film { public string _name; public Film(string name) { this._name = name; } } public class Cinema { public Room _room; public Film _film; public Cinema(Room room, Film film) { this._room = room; this._film = film; } public object Clone() { return MemberwiseClone(); //对引用类型实施浅复制 } }
3. 복사 후 효과 테스트
①복사하기 전에 원본 객체의 값 유형 및 참조 유형 멤버의 값을 인쇄합니다.
②복사 원본 객체, 복사된 객체의 값 유형과 참조 유형 멤버의 값을 출력
③ 원본 객체의 값을 변경하고 원본 객체의 값 유형과 참조 유형 멤버의 값을 다시 출력
④ 복사된 객체의 값타입과 참조타입 멤버를 다시 출력 Value
static void Main(string[] args) { Room room1 = new Room(60); Film film1 = new Film("家园防线"); Cinema cinema1 = new Cinema(room1, film1); Cinema cinema2 = (Cinema)cinema1.Clone(); Console.WriteLine("拷贝之前,结构成员的字段值为{0},引用类型成员的字段值为{1}", cinema1._film._name,cinema1._room._maxSeat); Console.WriteLine("拷贝之后,新的结构成员的字段值为{0},引用类型成员的字段值为{1}", cinema2._film._name, cinema2._room._maxSeat); //修改拷贝之前引用类型的字段值 cinema1._film._name = "极品飞车"; cinema1._room._maxSeat = 80; Console.WriteLine("修改之后,结构成员的字段值为{0},引用类型成员的字段值为{1}", cinema1._film._name, cinema1._room._maxSeat); Console.WriteLine("修改之后,新的结构成员的字段值为{0},引用类型成员的字段值为{1}", cinema2._film._name, cinema2._room._maxSeat); Console.ReadKey(); }
분석:
얕은 복사의 핵심은 참조타입이 관리되는 힙의 개체 인스턴스를 가리키는 개체 참조입니다. 원래 해당 참조 유형의 값을 변경하면 복사된 객체에 영향을 미칩니다.
2. Deep Copy
1. "Deep Copy"란
참조 멤버가 가리키는 객체를 복사하고 원본 객체 인스턴스에 포함된 값을 할당합니다. 관리되는 힙 데이터를 삭제한 다음 관리되는 힙에 새 개체 인스턴스를 만듭니다.
2. 각 객체 멤버를 복사하여 딥 카피
public object Clone() { Room room = new Room(); room._maxSeat = this._room._maxSeat;//复制当前引用类型成员的值到新对象 Film film = this._film; //值类型直接赋值 Cinema cinema = new Cinema(room, film); return cinema; }
3. 직렬화 및 역직렬화를 통해 딥 카피도 가능합니다
public object Clone1() { BinaryFormatter bf = new BinaryFormatter(); MemoryStream ms = new MemoryStream(); bf.Serialize(ms, this); //复制到流中 ms.Position = 0; return (bf.Deserialize(ms)); }
4. 직렬화 및 역직렬화 딥 카피를 수행하지만 모든 클래스는 [Serialized]로 표시되어야 합니다. 테스트 코드는 다음과 같습니다.
[Serializable] public class Room { public int _maxSeat; public Room() {} public Room(int maxSeat) { this._maxSeat = maxSeat; } } [Serializable] public struct Film { public string _name; public Film(string name) { this._name = name; } } [Serializable] public class Cinema { public Room _room; public Film _film; public Cinema(Room room, Film film) { this._room = room; this._film = film; } //浅拷贝 //public object Clone() //{ // return MemberwiseClone(); //对引用类型实施浅复制 //} //深拷贝 对每个对象成员进行复制 public object Clone() { Room room = new Room(); room._maxSeat = this._room._maxSeat;//复制当前引用类型成员的值到新对象 Film film = this._film; //值类型直接赋值 Cinema cinema = new Cinema(room, film); return cinema; } //使用序列化和反序列化进行复制 public object Clone1() { BinaryFormatter bf = new BinaryFormatter(); MemoryStream ms = new MemoryStream(); bf.Serialize(ms, this); //复制到流中 ms.Position = 0; return (bf.Deserialize(ms)); } }
5 .<를 복사한 후 효과를 테스트합니다. 🎜>
① 복사하기 전에 원본 객체의 값 유형 및 참조 유형 멤버의 값을 인쇄합니다.② 원본 객체 복사본의 경우 값 유형 및 참조 유형 멤버의 값을 인쇄합니다. 복사된 객체의
③원본 객체의 값을 변경하고 원본 객체의 값 유형 및 참조 유형 멤버를 다시 인쇄
④복사된 객체의 값 유형 및 참조 유형 멤버를 다시 인쇄
static void Main(string[] args) { Room room1 = new Room(60); Film film1 = new Film("家园防线"); Cinema cinema1 = new Cinema(room1, film1); Cinema cinema2 = (Cinema)cinema1.Clone1(); Console.WriteLine("拷贝之前,结构成员的字段值为{0},引用类型成员的字段值为{1}", cinema1._film._name,cinema1._room._maxSeat); Console.WriteLine("拷贝之后,新的结构成员的字段值为{0},引用类型成员的字段值为{1}", cinema2._film._name, cinema2._room._maxSeat); //修改拷贝之前引用类型的字段值 cinema1._film._name = "极品飞车"; cinema1._room._maxSeat = 80; Console.WriteLine("修改之后,结构成员的字段值为{0},引用类型成员的字段值为{1}", cinema1._film._name, cinema1._room._maxSeat); Console.WriteLine("修改之后,新的结构成员的字段值为{0},引用类型成员的字段值为{1}", cinema2._film._name, cinema2._room._maxSeat); Console.ReadKey(); }