NGMsoftware

NGMsoftware
로그인 회원가입
  • 매뉴얼
  • 학습
  • 매뉴얼

    학습


    C# C# 클래스의 깊은 복사와 얕은 복사 (Deep copy and Shallow copy)

    페이지 정보

    본문

    안녕하세요. 소심비형입니다. 정말 오랫만에 C# 관련해서 글을 쓰네요. 이전 글을 보니 작년 8월이군요. 지금 생각해보니 작년 8월에 이직해서 첫 출근했던게 기억납니다. 그래도 안짤리고 무사히 한해를 넘기다니... 정말 다행이네요^^;

     

    그동안 Java와 ASP.NET관련 글들을 정리하고, 앞으로 어떻게 먹고 살아가야 하나를 고민하면서 자마린도 해보고 R도 좀 기웃거려보고 했습니다. 하지만, 회사와 가정을 무난하게 운영하면서 다른 무언가를 한다는게 쉽지 않군요. 물론, 이 홈페이지를 어떻게든 운영하면서 끌고 나가보려는 노력도 포함되어 있습니다.

     

    잡소리는 이만 줄이고, 오늘은 깊은 복사와 얕은 복사에 대해서 알아볼께요. 사실 이제와서 C#이나 Java 또는 ASP.NET, JSP, PHP같은 것을 다룬다는게 의미 없다는걸 알고 있습니다. 이미 오래된 기술들이고, 관련 커뮤니티와 수많은 레퍼런스들이 존재하고 있기 때문이죠. 그래도 이렇게 정리 하면서 스스로 공부도 하고 각 언어들의 버전이 올라갈 때마다 새로운 기술들을 사용해볼 수 있어서 좋습니다. 이렇게 홈페이지에 정리하지 않는다면 신기술이 나오더라도 딱히 손이 가질 않더라구요^^;

     

    C#의 참조 타입에 대한 복사는 기본적으로 얕은 복사가 이루어집니다. 이 홈페이지의 NG.Client.Framework의 메뉴 관련 모듈의 경우에는 대부분 ICloneable을 상속 받아서 깊은 복사가 구현되어 있습니다. 우리는 여기에서 한가지 혼동이 되는 부분이 발생됩니다. ICloneable을 상속 받으면 맴버인 public object Clone() 메소드를 구현해야 하는데, 깊은 복사인 Copy() 메소드와 기능이 명확하게 구분이 되지 않는다는 것입니다. 물론, Copy() 메소드의 경우 구현하지 않아도 됩니다. 다만, 용도에 따라 인터페이스를 구현하느냐 안하느냐의 차이가 발생하고, 인터페이스를 구현함으로써 얻어지는 이점과 단점이 모두 존재합니다. 물론, 여기에서 말하는 단점은 설계 과정에서 해결이 가능합니다.

     

    아래 예제를 통해 깊은 복사와 복제, 얕은 복사에 대해서 알아보겠습니다.

    MainApp.cs

    using System;
     
    namespace DeepCopy
    {
        public enum Sex { Male, Female }
        public class Ironman : ICloneable
        {
            public Sex Sex { get; set; }
            public int SerialNo { get; set; }
            public int Power { get; set; }
            public Ironman() : this(Sex.Male) { }
            public Ironman(Sex sex) { this.Sex = sex; this.SerialNo = 0; this.Power = 0; }
            public object Clone() { return new Ironman(); }
            public Ironman Copy()
            {
                return new Ironman() { Sex = this.Sex, SerialNo = this.SerialNo, Power = this.Power };
            }
        }
        public class MainApp
        {
            static void Main(string[] args)
            {
                Console.WriteLine("객체의 얕은 복사 ======================");
                Ironman 아이언맨1호 = new Ironman();
                아이언맨1호.Sex = Sex.Male;
                아이언맨1호.SerialNo = 1;
                아이언맨1호.Power = 100;
                Ironman 아이언맨2호 = 아이언맨1호;
                아이언맨2호.Sex = Sex.Female;
                아이언맨2호.SerialNo = 2;
                아이언맨2호.Power = 200;
                Console.WriteLine($"{아이언맨1호.Sex}, {아이언맨1호.SerialNo}, {아이언맨1호.Power}");
                Console.WriteLine($"{아이언맨2호.Sex}, {아이언맨2호.SerialNo}, {아이언맨2호.Power}");
                Console.WriteLine();
                Console.WriteLine("객체의 깊은 복사 ======================");
                Ironman 헐크버스터1호 = new Ironman();
                헐크버스터1호.Sex = Sex.Female;
                헐크버스터1호.SerialNo = 1;
                헐크버스터1호.Power = 500;
                Ironman 헐크버스터2호 = 헐크버스터1호.Copy();
                Console.WriteLine("기본 구조와 데이터를 모두 복사!");
                Console.WriteLine($"{헐크버스터1호.Sex}, {헐크버스터1호.SerialNo}, {헐크버스터1호.Power}");
                Console.WriteLine($"{헐크버스터2호.Sex}, {헐크버스터2호.SerialNo}, {헐크버스터2호.Power}");
                헐크버스터2호.Sex = Sex.Male;
                헐크버스터2호.SerialNo = 2;
                헐크버스터2호.Power = 1000;
                Console.WriteLine("데이터 변경!");
                Console.WriteLine($"{헐크버스터1호.Sex}, {헐크버스터1호.SerialNo}, {헐크버스터1호.Power}");
                Console.WriteLine($"{헐크버스터2호.Sex}, {헐크버스터2호.SerialNo}, {헐크버스터2호.Power}");
                Console.WriteLine();
                Console.WriteLine("객체의 깊은 복제 ======================");
                Ironman 프로토타입1호 = new Ironman();
                프로토타입1호.Sex = Sex.Female;
                프로토타입1호.SerialNo = 1;
                프로토타입1호.Power = 500;
                Ironman 프로토타입2호 = 프로토타입1호.Clone() as Ironman;
                Console.WriteLine("기본 구조만 복사!");
                Console.WriteLine($"{프로토타입1호.Sex}, {프로토타입1호.SerialNo}, {프로토타입1호.Power}");
                Console.WriteLine($"{프로토타입2호.Sex}, {프로토타입2호.SerialNo}, {프로토타입2호.Power}");
                프로토타입2호.Sex = Sex.Male;
                프로토타입2호.SerialNo = 2;
                프로토타입2호.Power = 1000;
                Console.WriteLine("데이터 변경! 깊은 복사와 차이가 없다!");
                Console.WriteLine($"{프로토타입1호.Sex}, {프로토타입1호.SerialNo}, {프로토타입1호.Power}");
                Console.WriteLine($"{프로토타입2호.Sex}, {프로토타입2호.SerialNo}, {프로토타입2호.Power}");
                Console.WriteLine();
                Console.ReadKey(true);
            }
        }
    }

     

     

    위의 예제를 통해 좀 더 명확하게 복사와 복제에 대해서 이해할 수 있어야 합니다. 얕은 복사는 참조 타입의 주소를 복사하므로 실제 데이터가 변경되지 않습니다. 따라서, 아이언맨1호와 2호는 같습니다. 몸은 2개지만 컨트롤러 하나로 둘다 조종한다고 이해하시면 됩니다. 서로 같은 주파수를 가지고 있기 때문이죠.

     

    깊은 복사는 새로운 인스턴스를 생성하고 자신의 구조와 데이터를 이관합니다. 헐크버스터1호와 2호는 서로 다른 공간(메모리 영역)에 존재하게 됩니다. 따라서 헐크버스터2호의 데이터가 변경되더라도 1호는 영향을 받지 않게 됩니다. 

     

    마지막으로 복제는 ICloneable을 상속 받아서 구현하였으며, 이는 객체의 기본 구조만을 복사합니다. 데이터는 초기화됩니다. 사실 이 부분에 대해서는 아직까지도 어떻게 역할을 구분하고 어느 범위까지 관여해야 하는지가 명확하지 않습니다. 때론, 설계할 때 또는 구현할 때 범위가 정해지기도하고 구현되는 모듈의 특성에 따라 역할이 결정될 때도 있습니다.

     

    좀 더 쉽게 설명하자면, 박근혜와 김무성이 있다고 생각해봅시다. 이 둘은 역사 교과서 국정화와 대국민 사기꾼이라는 공통점이 있습니다. 하지만, 여성과 남성으로 다른 특성을 가지고 있는데 이런 경우에는 클론입니다. 태어날 때부터 정해진 특성은 복사할 수 없죠. 하지만, 김무성이 여자로 특성이 바뀐다면 깊은 복사라고 할 수 있습니다. 정치에 관심이 없으신 분이라면 아마 더 이해하기 어려울수도 있겠네요-_-;

     

    아래는 결과를 나타내고 있습니다.

    05WfjpE.png

     

     

    다음 시간에...

    • 네이버 공유하기
    • 페이스북 공유하기
    • 트위터 공유하기
    • 카카오스토리 공유하기
    추천0 비추천0

    댓글목록

    등록된 댓글이 없습니다.