깊은 복사(Deep Copy) VS 얕은 복사(Shallow Copy)

Unity3D/C# 2015. 10. 23. 12:03
반응형

출처: http://www.devpia.com/Maeul/Contents/Detail.aspx?BoardID=18&MAEULNO=8&no=1289&page=29

1. Assign (할당)

 

보통 생성된 객체를 다른 변수로 참조할 경우 assign(할당한다고 한다.

 

//Person 클래스가 있다고 가정한다.

Person person1 = new Person();

Person person2 = person1 // person1  person2 는 동일한 객체 참조값을 가지게 된다.

 

  

 

이렇게 Assign 된 두 객체는 완전히 동일한 객체가 된다.

 person2 의 멤버변수 값을 변경하면 person1 도 변경된것이나 다름없다.

(동일한 객체를 참조하기 때문에 Person 객체의 변경은 person1,person2 두 변수 모두에게 영향이 있다)

Assign은 객체의 복사와는 조금은 무관한 주제이긴 하나 객체복사를 알아보기 이전에 잠시 살펴보았다.

 

 

2. 깊은 복사 / 얕은 복사

 

이제 본격적으로 객체의 복사(복제)에 대해 알아 보자.

객체가 복사된 다는 것은 기존 객체와 같은 값을 가지는 새로운 객체를 만든다는 것이다.

객체는 멤버(필드)를 가진다멤버는 값 형식일 수도 있고 참조형식일 수도 있다.

객체가 가진 값 형식(Value Type) 과 참조형식(Reference Type) 의 복제 방식에 따라

얕은복사와 깊은 복사로 개념이 나뉜다.

 

 

2.1 Shallow Copy (얕은 복사)

 

얕은 복사는 단순 복사라고도 한다.

얕은 복사는 객체가 가진 멤버의 값들을 새로운 객체로 복사를 하는데 만일 객체가 참조타입의 멤버를 가지고 있다면

참조값만 복사된다. 아래의 클래스를 보자.

 

class Person

{

        public string name;

        public int age;

        public Car car;              

 

        public string Name

        {

               get { return this.name; }

               set { this.name = value; }

        }

        public int Age

        {

               get { return this.age; }

               set { this.age = value; }

        }

        public Car Car

        {

               get { return this.car; }

               set { this.car = value; }

        }

 

        public object ShallowCopy()

        {

               return this.MemberwiseClone();

        }

}

 

class Car 

{             

        public string model;                 

        public string Model

        {

               get { return this.model; }

               set { this.model = value; }

        }

}

 

Person 클래스와 Car 클래스가 있으며 Person 클래스는 Car 를 포함하고 있다.

Person 이 가지고 있는 Car 가 바로 객체가 가진 참조타입의 멤버인 것이다.

이제 얕은 복사를 해 보자.

닷넷 프레임워크가 제공해 주는 Object 클래스의 MemberwiseClone 메서드를 이용해 해보자

(MemberwiseClone 메서는 protected 기 때문에 바로 노출할 수 없다)

 

static void Main(string[] args)

{

        //원본 객체(person)

        Person person = new Person();

        person.Name = "홍길동";

        person.Age = 33;

        person.Car = new Car();

        person.Car.Model = "뉴카이런";

                      

        //얕은 복사된 객체(객체안의 참조타입의 멤버는 참조값이 복사될 뿐이다)

        Person person2 = (Person) person.ShallowCopy();

        person2.Name = "진시황";

        person2.Age = 55;

        person2.Car.Model = "뉴산타페";

 

        //두 객체 비교 - 두 객체간 동일한 Car 참조를 가지고 있다

        WriteLine(person);

        WriteLine(person2);   

}

 

static void WriteLine(Person person)

{

        Console.WriteLine(

String.Format("이름:{0},나이{1},자동차모델:{2}",

person.Name,person.Age,person.Car.Model)

        );

}

 

 

 

얕은 복사를 수행한 결과 화면은 다음과 같다.

 

 

 

 

결과에서 알수 있듯이 person1 객체가 복사된 person2 는 새로운 객체이긴 하나 참조 멤버인 Car 에 대해서는 동일한 객체참조를 가지고 있다.

(person2 에서 Car  Model 값을 변경하니 person1 도 변경되었다)

 

 

 

 

얕은 복사 (단순 복사) - MSDN

단순 복사본은 원본 개체와 같은 형식의 새로운 인스턴스를 만든 다음 원본 개체의 비정적 필드를 복사합니다필드가 값 형식인 경우 필드의 비트별 복사가 수행됩니다필드가 참조 형식인 경우 참조는 복사되지만 참조되는 개체는 복사되지 않습니다따라서 원본 개체 안의 참조와 복제 안의 참조가 동일한 개체를 가리킵니다

 

 

 

2.2 Deep Copy (깊은 복사)

 

깊은 복사는 전체 복사라도 한다.

얕은 복사와 달리 객체가 가진 모든 멤버(값형식이든 참조형식이든)가 복사되는 것을 말한다.

객체가 참조타입의 멤버를 포함할 경우 참조값의 복사가 아닌 참조된 객체 자체가 복사되는 것을 깊은 복사라 한다.

결국 아래와 같이 실현하고자 함이다.

 

 

person1  person2 는 완전히 서로 다른 객체가 되도록 하는 것이다.

 

   

ICloneable 인터페이스

닷넷 프레임웍에서는 이와 같이 깊은 복사를 위해 ICloneable 인터페이스를 제공한다.

이 인터페이스는 단 하나의 메서드Clone 메서드가 정의되어 있다.

(프레임웍 내의 클래스라이브러리에 수많은 클래스들이 이 인터페이스를 구현하여 실제

객체 복사를 지원한다)

만일 우리의 Person/Car 시나리오를 깊은 복사 가능 객체로 만들려면 프레임웍의 클래스들이 그렇게 했듯

IConeable 를 직접 구현할 수 밖에 없다.

 

그럼 직접 깊은 복사를 수행하는 코드를 만들어 보자.

일단 Person 객체를 ICloneable 인터페이스를 참조하도록 하고 다음과 같이 Clone 메서드를 구현하자.

 

public object Clone()

{

        Person person = new Person();

        person.name = this.name;

        person.age = this.age;

        person.car = new Car();

        person.car.model = person.car.model;

 

        return person;

}

 

Person 객체의 Clone 메서드는 결국 자기 자신의 멤버값들을 그대로 대입시킨 새로운 Person 객체를 생성해서 반환하도록 한다.

이 때 Car 객체 역시 new 로 완전히 새로운 객체로 생성하도록 하는게 핵심이다.

 

이제 결과 확인을 위해 다음과 같이 테스트 코드를 일부 수정한다.

 

static void Main(string[] args)

{

        //원본 객체(person)

        Person person = new Person();

        person.Name = "홍길동";

        person.Age = 33;

        person.Car = new Car();

        person.Car.Model = "뉴카이런";

                      

        //깊은 복사된 객체

        Person person3 = (Person) person.Clone();

        person3.Name = "진시황";

        person3.Age = 55;

        person3.Car.Model = "뉴산타페";

 

        //두 객체 비교 - 두 객체간 서로 다른 Car 참조를 가지고 있다

        WriteLine(person);

        WriteLine(person3);                  

}

static void WriteLine(Person person)

{

        Console.WriteLine(

String.Format("이름:{0},나이{1},자동차모델:{2}",

person.Name,person.Age,person.Car.Model)

        );

}

 

 

이 코드를 수행한 깊은 복사의 결과는 다음과 같다.

 

 

 

앞서 얕은 복사의 결과 화면과는 달리 자동차 모델의 값 변경은 서로 영향을 미치지 않는다.

 

참고>

ICloneable  Clone 메서드가 반드시 깊은 복사를 수행해야만 하는 것은 아니다.

필요한 경우 얕은 복사를 위한 Clone 가 구현될 수도 있다.

(아시죠그때 그때 상황에 맞도록 사용하는거 ^^;)

 

 

 

3. 프로토 타입 패턴

 

복사(복제)과 관련된 디자인 패턴은 대표적으로 프로토 타입 패턴이 있다.

프로토타입 패턴은 복제를 통한 객체 생성을 지원함으로써 객체생성 과정의 캡슐화 및 객체생성의 편리성을 제공하는 기법이다.

반응형

'Unity3D > C#' 카테고리의 다른 글

string의 기본값 null  (0) 2018.08.21
LinkedList  (0) 2016.03.19
[Design Pattern] Factory Method  (0) 2015.09.02
Delegate를 메서드 파라미터로 전달  (0) 2015.04.06
Func<TResult> Delegate  (0) 2015.04.06
: