아카이빙/C#

[C#] 얕은 복사와 깊은 복사

셩님 2018. 6. 18. 03:24

[C#] 얕은 복사와 깊은 복사

  • C#에서 클래스는 태생적으로 참조 형식이다.

  • 스택영역에 있는 참조가 힙영역에 할당된 객체의 메모리를 가리킨다.

  • SampleClass a = new SampleClass(); SampleClass b = a;와 같은 코드에서는 a와 b는 서로 같은 객체를 가리키고 있다.

  • 같이 a의 필드를 변경하면 b의 필드도 변경된다. 이를 얕은 복사(Shallow Copy)라 한다.

  • 반면, a 인스턴스를 힙영역에 새로운 객체를 할당하여 복사하는 것은 깊은 복사(Deep Copy)이다.

간단한 복사

using System;

class TestDeepCopy
{
   public int field1;
   public int field2;

   public TestDeepCopy DeepCopy()
  {
       TestDeepCopy copy = new TestDeepCopy();
       copy.field1 = this.field1;
       copy.field2 = this.field2;

       return copy;
  }

   public void Print()
  {
       Console.WriteLine("{0} {1}", field1, field2);
  }
}

테스트

class MainApp
{
   static void Main(string[] args)
  {
       Console.WriteLine("Shallow Copy");
      {
           TestDeepCopy source = new TestDeepCopy();
           source.field1 = 10;
           source.field2 = 20;

           TestDeepCopy target = source;
           target.field2 = 30;

           source.Print();
           target.Print();
      }

       Console.WriteLine("Deep Copy");
      {
           TestDeepCopy source = new TestDeepCopy();
           source.field1 = 10;
           source.field2 = 20;

           TestDeepCopy target = source.DeepCopy();
           target.field2 = 30;

           source.Print();
           target.Print();
      }
  }
}

결과

Shallow Copy
10 30
10 30
Deep Copy
10 20
10 30

ICloneable 인터페이스를 활용한 깊은 복사

class TestDeepCopy : ICloneable
{
   public int field1;
   public int field2;

   public object Clone()
  {
       TestDeepCopy copy = new TestDeepCopy();
       copy.field1 = this.field1;
       copy.field2 = this.field2;

       return copy;
  }

   public void Print()
  {
       Console.WriteLine("{0} {1}", field1, field2);
  }
}
  • .Net 프레임워크에는 ICloneable이라는 인터페이스가 있다.

  • 깊은 복사 기능을 위한 Clone() 메소드 하나 만을 갖고 있음.

  • 호환성을 위해 깊은 복사를 하려면 ICloneable 인터페이스를 사용하는 게 좋다.

  • TestDeepCopy target = source.Clone as TestDeepCopy 

참조형 변수를 필드로 갖는 클래스의 복사

  • C#에서 클래스는 참조형이라고 얘기했었다.

  • 예컨대, A라는 클래스에서 B 클래스를 필드로 갖는다고 하자.

class Field
{
   public int value;
   public Field(int value)
  {
       this.value = value;
  }

   public override string ToString()
  {
       return value.ToString();
  }
}

class Test
{
   public int field1;
   public Field field2;

   public void Print()
  {
       Console.WriteLine("{0} {1}", field1, field2);
  }
}
  • 이런 경우에는 Clone 메소드에서 Test copy = new Teest(); copy.field2 = this.field2와 같은 방식으로는 깊은 복사가 되지 않는다.

  • 깊은 복사를 하려면 copy.field2 = new Field(this.field2.value);처럼 새 인스턴스를 만들어 줘야한다.

Clone 메소드 구현

class Test : ICloneable
{
   public int field1;
   public Field field2;

   public object ShallowCopy()
  {
       //MemberwiseClone()은 System.Object의 메소드로 Shallow Copy를 만들어준다.
       return this.MemberwiseClone();
  }

   public object Clone()
  {
       Test copy = this.MemberwiseClone() as Test;
       copy.field2 = new Field(this.field2.value);
       return copy;
  }

   public void Print()
  {
       Console.WriteLine("{0} {1}", field1, field2);
  }
}

테스트

class MainApp
{
   static void Main(string[] args)
  {
       Console.WriteLine("Shallow Copy");
      {
           Test source = new Test();
           source.field1 = 10;
           source.field2 = new Field(20);

           Test target = source.ShallowCopy() as Test;
           target.field2.value = 30;

           source.Print();
           target.Print();
      }

       Console.WriteLine("Deep Copy");
      {
           Test source = new Test();
           source.field1 = 10;
           source.field2 = new Field(20);

           Test target = source.Clone() as Test;
           target.field2.value = 30;

           source.Print();
           target.Print();
      }
  }
}


참조

  • 뇌를 자극하는 C# 5.0 프로그래밍, 박상현, 한빛미디어


'아카이빙 > C#' 카테고리의 다른 글

[C#] 형변환과 is, as  (0) 2018.06.18
[C#] this() 생성자  (0) 2018.06.18
[C#] static 필드와 메소드  (0) 2018.06.18
[C#] 유니티에서 foreach 성능은 개선되었을까  (1) 2017.05.06
[C#] FieldInfo와 PropertyInfo  (0) 2017.05.06