C#에서는 값 형식(Value Type)과 참조 형식(Reference Type)을 서로 연결하기 위한 두 가지 메커니즘을 제공합니다. 바로 박싱(Boxing)과 언박싱(Unboxing)입니다. 박싱은 값 형식의 데이터를 개체(object) 형식으로 변환하는 과정이며, 언박싱은 그 반대로 박싱된 개체에서 원래 값 형식의 데이터를 다시 추출하는 과정을 의미합니다.
박싱과 언박싱의 주요 차이점
| 번호 | 구분 기준 | 박싱(Boxing) | 언박싱(Unboxing) |
|---|---|---|---|
| 1 | 구현 방식 | 값 형식을 감싸는 개체(object) 형식을 만들어 참조합니다. | 박싱된 개체로부터 원래 값을 꺼내어 반환합니다. |
| 2 | 저장 위치 | 스택(stack)에 저장된 값이 힙(heap) 메모리의 개체로 복사됩니다. | 힙 메모리에 저장된 개체의 값이 스택의 값 형식 변수로 복사됩니다. |
| 3 | 변환 유형 | 일반적으로 암시적(Implicit) 변환으로 처리됩니다. | 명시적(Explicit) 캐스팅이 반드시 필요합니다. |
| 4 | 성능 및 예외 | 새 개체를 힙에 할당하므로 가비지 컬렉션 부담이 늘어날 수 있습니다. | 형식이 일치하지 않으면 InvalidCastException이 발생합니다. |
박싱 vs 언박싱 예제 코드
다음 예제는 값 형식 변수를 박싱한 뒤 원래 변수의 값을 변경하고, 다시 언박싱하여 어떤 결과가 나타나는지 확인하는 코드입니다.
BoxingExample.cs
using System;
class BoxingExample {
static void Main(string[] args) {
int val = 119;
// 박싱(Boxing)
Object o = val;
// val의 값 변경
val = 120;
// 언박싱(Unboxing)
int x = (int)o;
Console.WriteLine("x의 값: " + x);
Console.WriteLine("값 형식 변수 val: " + val);
Console.WriteLine("개체 형식 변수 o: " + o);
}
}
실행 결과
x의 값: 119 값 형식 변수 val: 120 개체 형식 변수 o: 119
예제 해설
실행 결과에서 주목할 점은 박싱이 발생하는 순간 val의 값(119)이 힙 메모리의 개체 o로 복사된다는 것입니다. 따라서 이후 val을 120으로 변경해도 개체 o에는 아무런 영향이 없으며, 언박싱된 x 역시 여전히 119를 유지합니다.
박싱과 언박싱은 매우 편리하지만 힙 할당과 데이터 복사가 추가로 발생하기 때문에, 반복문처럼 자주 실행되는 코드에서는 성능 저하의 원인이 될 수 있습니다. 따라서 가능하면 제네릭(Generic) 컬렉션 등을 활용해 불필요한 박싱·언박싱을 줄이는 것이 좋습니다.