내용 보기

작성자

아롱이 (IP : 172.17.0.1)

날짜

2020-07-28 08:50

제목

[C#] 깊은 복사(Deep Copy) VS 얕은 복사(Shallow Copy)


기초부분은 다시 한번 다듬기(?) 위해 모든 언어의 공통적인 부분인 데이터 복사에 대해 간단하게 정리 한다.

  • 얕은 복사(Shallow Copy)
class CommonObj
{
    public int Id { get; set; }
    public string Address { get; set; }
}
class MyObj
{
    public string Name { get; set; }
    public int Age { get; set; }
    public CommonObj Common { get; set; }
    public object ShallowCopy()
    {
        return this.MemberwiseClone();
    }
}
 
// 원본
MyObj origin = new MyObj();
origin.Name = "원본 입니다.";
origin.Age = 17;
origin.Common = new CommonObj() { Address = "AAA", Id = 0 };
 
// 복사본
MyObj copy = (MyObj)origin.ShallowCopy();
cs

그림처럼 Shallow Copy로 데이터를 복사하면 복사된 객체는 서로 같은 참조된 필드를 공유하게 됩니다.대략적으로 위의 코드를 메모리 구조상으로 보면 다음과 같다.


이후 복사된 객체의 필드를 다음과 같이 변경해서 결과를 확인하면

class CommonObj
{
    public int Id { get; set; }
    public string Address { get; set; }
}
class MyObj
{
    public string Name { get; set; }
    public int Age { get; set; }
    public CommonObj Common { get; set; }
    public object ShallowCopy()
    {
        return this.MemberwiseClone();
    }
}
// 원본
MyObj origin = new MyObj();
origin.Name = "원본 입니다.";
origin.Age = 17;
origin.Common = new CommonObj() { Address = "AAA", Id = 0 };
 
// 복사본
MyObj copy = (MyObj)origin.ShallowCopy();
copy.Age = 18;
copy.Common.Address = "BBB";
copy.Common.Id = 1;
 
origin.Name = "원본 값 변경";
 
Console.WriteLine($"=====원본 출력[origin]===== ");
Console.WriteLine($"Name : {origin.Name}");
Console.WriteLine($"Age : {origin.Age}");
Console.WriteLine($"Common - Address : {origin.Common.Address}");
Console.WriteLine($"Id - Address : {origin.Common.Id}");
 
Console.WriteLine();
 
Console.WriteLine($"=====복사본(Shallow copy) 출력[copy]===== ");
Console.WriteLine($"Name : {copy.Name}");
Console.WriteLine($"Age : {copy.Age}");
Console.WriteLine($"Common - Address : {copy.Common.Address}");
Console.WriteLine($"Id - Address : {copy.Common.Id}");
 
// 출력결과
=====원본 출력[origin]===== 
Name : 원본 값 변경
Age : 17
Common - Address : BBB
Id - Address : 1

=====복사본(Shallow copy) 출력[copy]===== 
Name : 원본 입니다.
Age : 18
Common - Address : BBB
Id - Address : 1
cs

값 형식의 데이터는 서로 각각 힙에 할당되어 있는 주소를 참조해서 각 데이터가 원본 데이터, 복사본 데이터가 서로 다르게 출력되는 것을 볼 수 있습니다.
(값 형식의 데이터는 bit-by-bit copy(비트 별 복사)로 수행 됩니다.)
하지만 참조형식의 Common필드는 서로 같은곳을 참조하고 있기 때문에 원본 데이터까지 같이 변경된 것을 볼 수 있습니다.

그런데 참조형식의 필드는 서로 같은곳을 참조한다고 했는데 Name값을 보면 서로 다른 값이 출력되어 보여집니다.

이것은 원본의 origin.Name = "원본 값 변경";
코드가 실행될때 "원본 값 변경"문자열 객체를 새로 Heep에 할당하고 origin객체의 Name필드가 해당 주소를 가리키도록 변경이 되서 복사본 데이터에 영향을 주지 않게 된 것입니다.

위 결과 처럼 Shallow Copy시 참조 객체는 Deep Copy되지 않고 서로 같은 객체를 바로 보게 됩니다.


  • 깊은 복사(Deep Copy)

깊은 복사는 참조 객체까지 모두 복사해서 서로 다른 주소를 참조하게 됩니다.

class CommonObj
{
    public int Id { get; set; }
    public string Address { get; set; }
}
 
class MyObj
{
    public string Name { get; set; }
    public int Age { get; set; }
    public CommonObj Common { get; set; }
 
    public object ShallowCopy()
    {
        return this.MemberwiseClone();
    }
 
    public object DeepCopy()
    {
        MyObj myObj = new MyObj();
        myObj.Name = Name;
        myObj.Age = Age;
        myObj.Common = new CommonObj() { Id = Common.Id, Address = Common.Address };
 
        return myObj;
    }
}
 
// 원본
MyObj origin = new MyObj();
origin.Name = "원본 입니다.";
origin.Age = 17;
origin.Common = new CommonObj() { Address = "AAA", Id = 0 };
 
// 복사본
MyObj copy = (MyObj)origin.DeepCopy();
copy.Age = 18;
copy.Common.Address = "BBB";
copy.Common.Id = 1;
 
origin.Name = "원본 값 변경";
 
Console.WriteLine($"=====원본 출력[origin]===== ");
Console.WriteLine($"Name : {origin.Name}");
Console.WriteLine($"Age : {origin.Age}");
Console.WriteLine($"Common - Address : {origin.Common.Address}");
Console.WriteLine($"Id - Address : {origin.Common.Id}");
 
Console.WriteLine();
 
Console.WriteLine($"=====복사본(Shallow copy) 출력[copy]===== ");
Console.WriteLine($"Name : {copy.Name}");
Console.WriteLine($"Age : {copy.Age}");
Console.WriteLine($"Common - Address : {copy.Common.Address}");
Console.WriteLine($"Id - Address : {copy.Common.Id}");
 
// 출력결과
=====원본 출력[origin]===== 
Name : 원본 값 변경
Age : 17
Common - Address : AAA
Id - Address : 0
 
=====복사본(Shallow copy) 출력[copy]===== 
Name : 원본 입니다.
Age : 18
Common - Address : BBB
Id - Address : 1
cs

위 Deep Copy가 실행되고 난 후 메모리 구조를 보면 다음과 같습니다.


참조 형식의 Common객체까지 개별 인스턴스 복사
되므로  원본 데이터, 복사본 데이터가 서로 다르게 출력되는 것을 볼 수 있습니다.

출처1

출처2




2020-07-28 09:02
자바스크립트의 깊은 복사와 얖은 복사 - https://medium.com/watcha/%EA%B9%8A%EC%9D%80-%EB%B3%B5%EC%82%AC%EC%99%80-%EC%96%95%EC%9D%80-%EB%B3%B5%EC%82%AC%EC%97%90-%EB%8C%80%ED%95%9C-%EC%8B%AC%EB%8F%84%EC%9E%88%EB%8A%94-%EC%9D%B4%EC%95%BC%EA%B8%B0-2f7d797e008a
아롱이
(172.17.0.1)
2020-07-28 09:01
참고 - https://aspdotnet.tistory.com/2122
아롱이
(172.17.0.1)
2020-07-28 09:01
참고 - https://m.blog.naver.com/adonise007/220578209008
아롱이
(172.17.0.1)
2020-07-28 09:01
참고 - https://www.sysnet.pe.kr/Default.aspx?mode=2&sub=0&detail=1&wid=11220#14310
아롱이
(172.17.0.1)