오늘은 ICloneable 인터페이스를 이용해서 클래스를 복사하는 방법을 알아보자
프로젝트를 진행하던 중 Monster 클래스로 만든 객체를 복사하여 그 복사한 객체의 Hp를 감소시켰더니 같은 종류의 모든 몬스터의 Hp가 전부 감소되는 상황이 생겼다.
그 이유는 위에서 진행한 복사가 얕은 복사이기 때문이다.
얕은 복사란? 클래스 같은 객체를 참조형으로 복사하면 복사한 객체도 같은 주소를 가리켜 복사된 객체를 수정하면 원본 객체의 값도 수정되는 현상을 말한다.
그래서 클래스를 완벽하게 복제하려면 깊은 복사를 해야한다.
깊은 복사는 데이터의 값 전체를 복사한다고 생각하면 된다.
예를 들면 아래와 같다.
struct PersonStruct { public string name; public int age; public Person(int name, int age) { this.name = name; this.age = age; } } class PersonClass { public string name; public int age; public Person(int name, int age) { this.name = name; this.age = age; } } PersonStruct ps1 = new PersonStruct("a",1); PersonStruct ps2 = ps1; // 깊은 복사 ps1.age = 10; Console.WriteLine(ps2.age); // 1 PersonClass pc1 = new PersonClass("a",1); PersonClass pc2 = pc1; pc1.age = 10; Console.WriteLine(pc2.age); // 10
그럼 깊은 복사를 하기 위해선 값형 데이터인 구조체를 사용 해야되는건가? 라고 생각하면, 아니다. C#에서는 참조 형식을 깊은 복사로 복제하는 인터페이스를 제공한다.
Class를 깊은 복사하는 것은 생각보다 간단하다. 그것은 Class의 멤버 변수값을 모두 하나 씩 복사해주는 것이다.
이를 위해 C#은 ICloneable이라는 인터페이스를 제시한다.
public interface Icloneable { public object Clone(); // ...생략 }
쉽게 생각하면 Clone이라는 함수에 Class의 멤버 변수값을 모두 복사하는 기능을 구현하면 된다.
아래 코드는 직접 프로젝트를 진행하며 작성한 예시이다.
public class Monster : ICloneable { public int level { get; set; } public string name { get; set; } public int hp { get; set; } public int maxHp { get; set; } public int atk { get; set; } public bool isDead { get; set; } public Monster(int level, string name, int maxHp, int atk) { // ...생략 } public object Clone() { Monster newMonster = new Monster(0,"",0,0); newMonster.Level = this.level; newMonster.Name = this.name; newMonster.Hp = this.hp; newMonster.MaxHp = this.maxHp; newMonster.Atk = this.atk; newMonster.IsDead = this.isDead; return newMonster; } }
위와 같이 클래스에 인터페이스를 상속받고 Clone함수의 구현부를 작성해주면 쉽게 클래스를 깊은 복사할 수 있다!