본문 바로가기
언어(C, C++, C#)

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

by 적용1 2024. 11. 6.
728x90

깊은 복사(Deep Copy)

  • 깊은 복사는 객체의 값을 완전히 새로운 메모리 공간에 복사하는 방식이다. 따라서 원본 객체과 복사된 객체가 독립적으로 존재하고, 어느 한 쪽에서 변경이 일어나도 다른 쪽에 영향을 미치지 않는다.
  • 예시
public struct PersonStruct
{
	public string Name;
}

PersonStruct person1 = new PersonStruct { Name = "Tom" };
PersonStruct person2 = person1;	// 값 복사이므로 깊은 복사
person2.Name = "Bob";	// 깊은 복사이므로 복사해온 객체에 대해 영향 X

Console.WriteLine(person1.Name);	// 출력 : Alice
Console.WriteLine(person2.Name);	// 출력 : Bob

 

C#에서 구조체는 값 형식(value type)이므로 struct를 생성하면 새로운 객체의 값이 그대로 변수에 저장된다. 따라서 위와 같은 상황에서는 깊은 복사가 일어나게 되고, 하나의 값을 변경해도 다른 값에 영향을 미치지 않는다.

얕은 복사(Shallow Copy)

  • 얕은 복사는 객체의 참조 주소만 복사하는 방식이다. 따라서 원본 객체의 필드 중 참조 타입(배열, 리스트, 클래스와 같은 사용자 정의 객체 등)은 원본과 복사본이 같은 객체를 가리키게 된다.
    단, 값 타입 필드(int, bool, double 등)은 복사된다.
  • 예시
public class Person
{
	public string name;
}

Person person1 = new Person { Name = "Tom" };
Person person2 = person1;	// 얕은 복사가 일어남
person2.Name = "Bob";	// 얕은 복사이므로 같은 객체를 가리켜 person1에도 영향을 미침

Console.WriteLine(person1.Name);	// 출력 : Bob

 

C#에서 클래스는 참조 타입(reference type)이기 때문에 new 키워드를 통해 생성하면, 새로운 객체에 대한 참조가 반환된다. 따라서 위와 같이 객체를 new로 생성한 뒤 복사를 하게 되면, 참조만 복사하게 되는 얕은 복사가 일어나게 되고, 하나의 값을 바꾸면 다른 값에도 영향을 미치게 된다.

참조 형식을 깊은 복사로 복제하는 방법

직접 클래스 내에 구현

  • 참조 타입 필드를 새롭게 할당하고, 각 필드를 직접 복사해주는 방법이다.
  • 예시
public struct IdInfo
{
	public int IdNumber;
    
    public IdInfo(int IdNumber)
    {
    	this.IdNumber = IdNumber;
    }
}

public class Person
{
	public int Age;
    public string Name;
    public IdInfo IdInfo;
    
    public Person ShallowCopy()
    {
    	return (Person)this.MemberWiseClone();
    }
    
    public Person DeepCopy()
    {
    	Person other = (Person)this.MemberWiseClone();
        other.IdInfo = new IdInfo(IdInfo.IdNumber);
        other.Name = String.Copy(Name);
        return other;
    }
}

 

여기서 MemberwiseClone 메서드는 객체를 새로운 메모리에 할당하여 독립적인 객체로 복제하는 메서드이다.

 

이 과정에서 객체의 멤버도 복제되는데 값 형식(value type) 데이터는 제대로 깊은 복사가 되지만 참조 형식 데이터는 얕은 복사가 된다.

 

따라서 참조 형식의 멤버를 깊은 복사하는 코드를 별도로 구현해야 한다.

Serialize / Deserialize (직렬화 / 역직렬) 사용

  • JSON, Binary, XML 직렬화를 통해 객체를 완전히 새로운 인스턴스로 바꿀 수 있다.
  • 예시
public class Person
{
	public string Name;
    public List<string> Hobbies;
    
    public Person DeepCopy()
    {
    	var json = JsonSerializer.Serialize(this);	// json 형식으로 직렬화
        return JsonSerializer.Deserialize<Person>(json);	// 역직렬화
    }
}

 

이 때 JSON 직렬화를 사용하려면 클래스와 필드가 직렬화가 가능해야 하며, [Serializable] 또는 public 접근 수준을 가져야 한다.

728x90