성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Roll A Lisp In C - Reading ; https...
[정성태] Java - How to use the Foreign Funct...
[정성태] 제가 큰 실수를 했군요. ^^; Delegate를 통한 Bein...
[정성태] Working with Rust Libraries from C#...
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>닷넷 - Shallow Copy와 Deep Copy</h1> <p> 아래의 글을 읽고 잠깐 정리해 봅니다. ^^<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [.NET] Clone ; <a target='tab' href='http://www.jumptovb.net/397'>http://www.jumptovb.net/397</a> </pre> <br /> 닷넷의 System.Object 타입에는 MemberwiseClone 메서드가 (protected 접근자로) 기본 포함되어 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Object.MemberwiseClone 메서드 ; <a target='tab' href='https://learn.microsoft.com/en-us/dotnet/api/system.object.memberwiseclone'>https://learn.microsoft.com/en-us/dotnet/api/system.object.memberwiseclone</a> </pre> <br /> 도움말에도 나오지만 이 메서드를 수행하면 "shallow copy"를 한 객체를 반환해 줍니다. 따라서, MemberwiseClone 메서드와 동일한 기능을 구현하고 싶다면 다음과 같이 해주면 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > public class MyItem { public string Name; public int Age; public MyItem() { } // MemberwiseClone 메서드와 동일한 기능 (shallow copy 수행) public object Clone() { MyItem obj = new MyItem(); // 필드 수만큼 대입 obj.Name = this.Name; obj.Age = this.Age; return obj; } } </pre> <br /> 비교를 위해 Clone을 하지 않고 참조 값만 대입한 경우를 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > MyItem a = new MyItem { Name = "AAA", Age = 5 }; MyItem b = a; </pre> <br /> 이때의 메모리 상황은 다음과 같습니다.<br /> <br /> <img alt='shallow_deep_copy_1.png' src='/SysWebRes/bbs/shallow_deep_copy_1.png' /><br /> <br /> 하지만, shallow copy를 하게 되면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > MyItem a = new MyItem { Name = "AAA", Age = 5 }; MyItem b = a.Clone() as MyItem; </pre> <br /> 메모리 상황이 다음과 같이 바뀝니다.<br /> <br /> <img alt='shallow_deep_copy_2.png' src='/SysWebRes/bbs/shallow_deep_copy_2.png' /><br /> <br /> 위의 그림을 보면, MyItem에 해당하는 인스턴스 a, b는 힙에서 별도의 구역으로 존재하지만 그 내부에서 담고 있는 참조 값들은 여전히 동일한 객체를 가리키는 것을 확인할 수 있습니다. 즉, Name 필드의 참조 값이 0x1800으로 동일하게 "AAA" 객체를 가리키고 있는 것입니다. 반면, Age 필드는 MyItem 객체에 할당된 메모리 내에 값 자체(위의 예에서는 5)로 포함됩니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 그럼, deep copy는 뭘까요? 아래의 글에 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > XML Serializer를 이용한 값 복사 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/577'>http://www.sysnet.pe.kr/2/0/577</a> Dictionary<TKey, TValue>를 deep copy하는 방법 ; <a target='tab' href='http://www.sysnet.pe.kr/2/0/11157'>http://www.sysnet.pe.kr/2/0/11157</a> </pre> <br /> deep copy는 해당 객체를 복사할 때, 필드 유형이 참조 타입일지라도 별도의 메모리에 개별 인스턴스로 복사하는 것을 의미합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > MyItem a = new MyItem { Name = "AAA", Age = 5 }; MyItem b = <a target='tab' href='http://www.sysnet.pe.kr/2/0/11157'>CloneData</a><MyItem>(a); </pre> <br /> 따라서 위와 같이 객체를 복사하게 되면 메모리 상에 다음과 같은 식으로 표현이 바뀝니다.<br /> <br /> <img alt='shallow_deep_copy_3.png' src='/SysWebRes/bbs/shallow_deep_copy_3.png' /><br /> <br /> (주의: 위의 그림은 "Name" 필드의 타입이 string이 아닌 다른 참조 타입일 경우에만 올바릅니다. string 타입은 CLR 내부의 최적화로 인해 같은 문자열은 중복 할당하지 않도록 내부 풀로 관리되기 때문에 2개의 "AAA" 인스턴스가 아닌 1개의 "AAA" 문자열 인스턴스만 생성됩니다.)<br /> <br /> 차이점이 뭔지 아시겠죠? (MemberwiseClone을 비롯한) shallow copy는 해당 객체의 멤버 중 참조 타입은 공유하지만, deep copy는 참조 타입까지도 별도의 인스턴스로 분리시켜 복사하는 것을 의미합니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 참조 타입을 공유하는 shallow copy의 특성상 때로는 혼란이 올 수 있습니다. 가령, 다음과 같은 코드를 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > MyItem a = new MyItem { Name = "AAA", Age = 5 }; MyItem b = a.Clone() as MyItem; a.Name = "BBB"; Console.WriteLine(a.ToString()); // Name: BBB, Age: 5 Console.WriteLine(b.ToString()); // Name: AAA, Age: 5 </pre> <br /> 분명히 참조 값 필드가 공유된다고 했음에도 불구하고 결과를 보면 a 객체의 Name 필드 변경이 b 객체의 Name 필드와는 무관하게 동작하고 있습니다. 이것은 shallow copy가 된 2개의 인스턴스에 대한 메모리 상태를 따져보면 그 이유를 알 수 있습니다.<br /> <br /> 즉, a.Name = "BBB" 코드의 실행 전에는 a, b 인스턴스가 각각 다음과 같은 값을 가지며 힙에 할당된 상태입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // a 객체 8바이트 할당 (4바이트) a.Name: "AAA" 문자열 객체를 가리키는 주솟값 (4바이트) a.Age: 5 (값) // b 객체 8바이트 할당 (4바이트) b.Name: "AAA" 문자열 객체를 가리키는 주솟값 (4바이트) b.Age: 5 (값) </pre> <br /> 이후 a.Name = "BBB" 코드가 실행되면 CLR은 "BBB" 문자열 객체를 CLR Heap에 할당한 후 a.Name 변수가 그것을 가리키도록 바꿉니다. 그래서 다음과 같은 상태가 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > // a 객체 8바이트 할당 (4바이트) a.Name: "BBB" 문자열 객체를 가리키는 주솟값 (4바이트) a.Age: 5 (값) // b 객체 8바이트 할당 (4바이트) b.Name: "AAA" 문자열 객체를 가리키는 주솟값 (4바이트) b.Age: 5 (값) </pre> <br /> 정리해 보면, 위와 같은 상황에서는 shallow copy를 했지만 어느 한 쪽의 객체 값을 바꿔도 다른 쪽에 영향을 주지 않습니다. 그런데 이 효과가 마치 deep copy를 했을 때와 유사하므로 자칫 혼동해 버리면 나중에 문제가 발생할 수 있습니다.<br /> <br /> 위의 예제에서 shallow copy와 deep copy에 대한 차이를 부각시키려면 다음과 같이 참조 객체를 구성해 보면 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > [Serializable] <span style='color: blue; font-weight: bold'>public class MyText { public string Address; }</span> [Serializable] public class MyItem { <span style='color: blue; font-weight: bold'>public MyText Text;</span> public string Name; public int Age; public MyItem() { } public object Clone() { return this.MemberwiseClone(); } public override string ToString() { return string.Format("Name: {0}, Age: {1}, Address: {2}", Name, Age, Text.Address); } } </pre> <br /> 이렇게 한 후 다음과 같이 shallow copy를 하고 값을 변경해 보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > MyItem a = new MyItem { Name = "AAA", Age = 5, Text = new MyText { Address = "KOR" } }; MyItem b = <span style='color: blue; font-weight: bold'>a.Clone() as MyItem;</span> <span style='color: blue; font-weight: bold'>a.Text.Address = "US";</span> Console.WriteLine(a.ToString()); // Name: AAA, Age: 5, <span style='color: blue; font-weight: bold'>Address: US</span> Console.WriteLine(b.ToString()); // Name: AAA, Age: 5, <span style='color: blue; font-weight: bold'>Address: US</span> </pre> <br /> a 객체의 Text 변화가 b 객체의 Text 변화에도 영향을 미쳤음을 볼 수 있습니다. 비교를 위해 이것을 deep copy한 것으로 바꿔보면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > MyItem a = new MyItem { Name = "AAA", Age = 5, Text = new MyText { Address = "KOR" } }; MyItem b = <span style='color: blue; font-weight: bold'>CloneData<MyItem>(a);</span> <span style='color: blue; font-weight: bold'>a.Text.Address = "US";</span> Console.WriteLine(a.ToString()); // Name: AAA, Age: 5, <span style='color: blue; font-weight: bold'>Address: US</span> Console.WriteLine(b.ToString()); // Name: AAA, Age: 5, <span style='color: blue; font-weight: bold'>Address: KOR</span> </pre> <br /> a와 b 내의 모든 필드들이 값/참조에 상관없이 개별 복사가 되어 있기 때문에 a 객체의 변화가 b 객체의 변화에 전혀 영향을 미치지 않습니다.<br /> <br /> 이러한 특징이 있기 때문에 shallow copy는 객체 간 복사가 가볍다는 장점이 있는 반면 어느 한 쪽의 변화가 다른 쪽에 영향을 미칠 수 있다는 단점이 있고, deep copy는 서로 영향을 주지 않는 장점이 있는 반면 객체 간 복사가 무거워진다는 단점이 있습니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 마지막으로, ICloneable 인터페이스에 대한 언급을 해야겠습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > ICloneable ; <a target='tab' href='https://learn.microsoft.com/en-us/dotnet/api/system.icloneable'>https://learn.microsoft.com/en-us/dotnet/api/system.icloneable</a> </pre> <br /> 마이크로소프트는 ICloneable 인터페이스에 대해 반드시 shallow copy를 한 객체를 반환해야 한다거나, deep copy를 한 객체를 반환해야 한다는 어떠한 명시도 하고 있지 않습니다. 심지어, 다음과 같이 모든 필드의 값을 복사한다고도 보장할 수 없습니다.<br /> <br /> <div style='BACKGROUND-COLOR: #ccffcc; padding: 10px 10px 5px 10px; MARGIN: 0px 10px 10px 10px; FONT-FAMILY: Malgun Gothic, Consolas, Verdana; COLOR: #005555'> It does not specify whether the cloning operation performs a deep copy, a shallow copy, or something in between. Nor does it require all property values of the original instance to be copied to the new instance.<br /> </div><br /> <br /> 즉, 이것은 해당 객체를 정의하는 개발자의 판단에 의존합니다. 이로 인해 문서에는 다음과 같은 언급도 있습니다.<br /> <br /> <div style='BACKGROUND-COLOR: #ccffcc; padding: 10px 10px 5px 10px; MARGIN: 0px 10px 10px 10px; FONT-FAMILY: Malgun Gothic, Consolas, Verdana; COLOR: #005555'> Because callers of Clone cannot depend on the method performing a predictable cloning operation, we recommend that ICloneable not be implemented in public APIs. <br /> </div><br /> <br /> 따라서, ICloneable은 그 객체를 구현한 개발자가 사용할 코드 범위 내에서 편의 상 정의하는 것이 권장될 뿐입니다. 굳이 ICloneable을 공용으로 노출하고 싶다면 문서를 꼼꼼하게 작성해야 하고, 향후 내부 변경을 해야 할 때 기존 구현 코드가 shallow였는지 deep이었는지 잘 살펴보고 구현을 맞춰주어야 할 필요가 있습니다. 또한 사용하는 입장에서도 ICloneable 인터페이스를 구현한 클래스의 객체를 복사할 때는 반드시 관련 문서를 읽어봐야 합니다.<br /> <br /> (<a target='tab' href='http://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=1141&boardid=331301885'>첨부 파일은 이 글의 예제</a>와 <a target='tab' href='http://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=1142&boardid=331301885'>다이어그램 원본 파일</a>을 포함합니다.)<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
1250
(왼쪽의 숫자를 입력해야 합니다.)