ABOUT ME

이 블로그에서는 개발자들이 하루하루 마주하는 코딩 문제들과 그 해결책에 대해 다룰 예정입니다. 이 블로그는 개발자들을 위한 팁과 지식을 공유하는 블로그입니다. 개발자들은 매일 새로운 도전과 문제를 마주하며, 이를 해결하기 위한 지식과 기술을 갖추고 있어야 합니다. 하루하루 코딩은 이러한 도전과 문제에 대한 해결책을 다루면서, 개발자들이 더욱 능숙한 코딩 실력을 갖출 수 있도록 도와줄 것입니다.

Today
Yesterday
Total
  • 얕은 복사 (Shallow Copy) 와 깊은 복사 (Deep Copy)
    C# 2023. 3. 7. 18:14

    C#에서 객체를 복사하는 방법에는 복사와 깊은 복사 두 가지가 있습니다. 얕은 복사는 객체의 참조를 복사하여 같은 객체를 참조하는 경우가 있어 원본 객체와 복사본의 값이 공유될 수 있습니다. 반면, 깊은 복사는 객체의 모든 내용을 복사하여 원본 객체와는 독립적인 새로운 객체를 만듭니다.


    얕은 복사 (Shallow Copy)

    얕은 복사(Shallow Copy)객체의 참조만 복사하여 같은 객체를 참조하게 합니다. 따라서 원본 객체나 복사본 중 하나를 수정하면 다른 객체도 영향을 받을 수 있습니다.

     

    아래는 얕은 복사를 수행하는 예시 코드입니다.

    class MyClass {
        public int[] arr;
        public MyClass(int[] arr) {
            this.arr = arr;
        }
    }
    
    MyClass obj1 = new MyClass(new int[] { 1, 2, 3 });
    MyClass obj2 = obj1;  // 얕은 복사
    
    obj2.arr[0] = 4;
    Console.WriteLine(obj1.arr[0]);  // 출력: 4

     

    위 코드에서 obj1 객체와 obj2 객체는 같은 메모리를 참조하게 됩니다. 따라서 obj2.arr[0] = 4; 코드로 obj2arr 배열을 수정하면 obj1arr 배열도 같이 수정되어 Console.WriteLine(obj1.arr[0]); 코드에서 4가 출력됩니다.

     

       obj1       obj2
    +--------+  +--------+
    |  arr   |  |  arr   |
    |--------|  |--------|
    |   -->--+--+--<--   |
    |   [1]  |  |   [1]  |
    |   [2]  |  |   [2]  |
    |   [3]  |  |   [3]  |
    +--------+  +--------+

    위의 ASCII 아트는 obj1 객체와 obj2 객체를 얕은 복사할 때의 메모리 상태를 나타냅니다. obj1 객체는 { 1, 2, 3 } 배열을 참조하고 있습니다. obj2 객체를 obj1 객체로 얕은 복사하면, obj2 객체는 obj1 객체와 같은 { 1, 2, 3 } 배열을 참조합니다. 이 때, obj2.arr[0] = 4; 코드로 obj2 객체의 arr 배열을 수정하면, obj1 객체도 같은 배열을 참조하고 있기 때문에 obj1.arr[0] 값도 4로 변경됩니다.

     


    깊은 복사 (Deep Copy)

    깊은 복사(Deep Copy)객체의 내용을 모두 복사하여 원본 객체와는 독립적인 새로운 객체를 만듭니다. 따라서 한 객체의 수정이 다른 객체에 영향을 주지 않습니다.

     

    아래는 깊은 복사를 수행하는 예시 코드입니다.

    class MyClass {
        public int[] arr;
        public MyClass(int[] arr) {
            this.arr = (int[]) arr.Clone();  // 배열 복사
        }
    }
    
    MyClass obj1 = new MyClass(new int[] { 1, 2, 3 });
    MyClass obj2 = new MyClass(obj1.arr);  // 깊은 복사
    
    obj2.arr[0] = 4;
    Console.WriteLine(obj1.arr[0]);  // 출력: 1

    위 코드에서 obj1 객체와 obj2 객체는 서로 독립적인 객체입니다. 따라서 obj2.arr[0] = 4; 코드로 obj2arr 배열을 수정해도 obj1arr 배열은 영향을 받지 않습니다. 따라서 `Console.WriteLine(obj1.arr[0]);` 코드에서 1이 출력됩니다.

     

    C#에서 객체를 깊은 복사하는 방법 중 하나는 ICloneable 인터페이스를 구현하는 것입니다. ICloneable 인터페이스는 Clone 메서드를 정의하고 있으며, 메서드를 구현하여 객체를 복사할 수 있습니다. 다음은 ICloneable 인터페이스를 사용하여 MyClass 클래스를 깊은 복사하는 예시 코드입니다.

    class MyClass : ICloneable {
        public int[] arr;
        public MyClass(int[] arr) {
            this.arr = arr;
        }
    
        public object Clone() {
            int[] newArr = (int[]) arr.Clone();  // 배열 복사
            return new MyClass(newArr);
        }
    }
    
    MyClass obj1 = new MyClass(new int[] { 1, 2, 3 });
    MyClass obj2 = (MyClass) obj1.Clone();  // 깊은 복사
    
    obj2.arr[0] = 4;
    Console.WriteLine(obj1.arr[0]);  // 출력: 1

     

    위 코드에서 MyClass 클래스는 ICloneable 인터페이스를 구현하도록 변경되었습니다. Clone 메서드에서는 arr 배열을 복사하여 새로운 배열을 만든 후, 새로운 MyClass 객체를 생성합니다. obj2 객체는 obj1 객체의 깊은 복사본이므로, obj2.arr[0] = 4; 코드로 obj2의 arr 배열을 수정해도 obj1의 arr 배열은 영향을 받지 않습니다.

     

    또한, C#에서는 System.Object 클래스의 MemberwiseClone 메서드를 사용하여 얕은 복사를 수행할 수 있습니다. 다음은 MemberwiseClone 메서드를 사용하여 MyClass 클래스를 얕은 복사하는 예시 코드입니다.

    class MyClass {
        public int[] arr;
        public MyClass(int[] arr) {
            this.arr = arr;
        }
    
        public object ShallowCopy() {
            return (MyClass) this.MemberwiseClone();
        }
    }
    
    MyClass obj1 = new MyClass(new int[] { 1, 2, 3 });
    MyClass obj2 = obj1.ShallowCopy();  // 얕은 복사
    
    obj2.arr[0] = 4;
    Console.WriteLine(obj1.arr[0]);  // 출력: 4

    위 코드에서 MyClass 클래스는 ShallowCopy 메서드를 정의합니다. 이 메서드에서는 MemberwiseClone 메서드를 호출하여 현재 객체를 얕은 복사한 후, 캐스팅하여 반환합니다. obj2 객체는 obj1 객체의 얕은 복사본이므로, obj2.arr[0] = 4; 코드로 obj2arr 배열을 수정하면 obj1arr 배열도 같이 수정됩니다.

     

       obj1       obj2
    +--------+  +--------+
    |  arr   |  |  arr   |
    |--------|  |--------|
    |   -->--+--+-->     |
    |   [1]  |  |   [4]  |
    |   [2]  |  |   [2]  |
    |   [3]  |  |   [3]  |
    +--------+  +--------+

    위의 ASCII 아트는 obj1 객체와 obj2 객체를 깊은 복사할 때의 메모리 상태를 나타냅니다. obj1 객체는 { 1, 2, 3 } 배열을 참조하고 있습니다. obj2 객체를 obj1 객체로 깊은 복사하면, obj2 객체는 { 1, 2, 3 } 배열을 복사하여 새로운 배열을 참조합니다. 이 때, obj2.arr[0] = 4; 코드로 obj2 객체의 arr 배열을 수정해도, obj1 객체는 이 배열과는 다른 { 1, 2, 3 } 배열을 참조하고 있기 때문에 영향을 받지 않습니다.

     


    얕은 복사깊은 복사는 객체를 복사할 때 발생하는 문제를 해결하기 위해 사용됩니다. 얕은 복사는 객체의 참조만 복사하여, 두 객체가 같은 메모리를 참조하게 되는 반면, 깊은 복사는 객체의 모든 값을 복사하여, 두 객체가 서로 다른 메모리를 참조하게 됩니다.

    얕은 복사는 단순한 참조 복사로 메모리 절약에 유용하지만, 객체를 수정할 때 원하지 않는 부작용을 일으킬 수 있습니다. 따라서 객체를 수정할 일이 있거나, 복사한 객체의 값을 변경하더라도 원본 객체에 영향을 주지 않아야 하는 경우에는 깊은 복사를 사용해야 합니다.

     

    C#에서는 배열, 리스트, 객체 등을 복사하는 방법으로 얕은 복사와 깊은 복사를 모두 제공합니다. 배열과 리스트의 경우 Array.Clone() 메서드나 List<T>.CopyTo() 메서드를 사용하여 깊은 복사를 할 수 있습니다. 객체의 경우 MemberwiseClone() 메서드를 사용하여 얕은 복사를 할 수 있으며, IClonable 인터페이스를 구현하여 객체를 깊은 복사할 수 있습니다.

     

    알맞은 복사 방법을 선택하면 메모리 관리와 객체 수정 등의 문제를 효과적으로 해결할 수 있습니다.

     

    댓글

Designed by Tistory.