요즘 유니티를 이용해 C#으로 코딩을 하시는 분들이 많은데, 간혹 면접을 보다보면 Boxing과 Unboxing에 대해 제대로 이해하지 못하는 분들이 많더군요. 이를 C/C++의 캐스팅 같은 것으로 생각하고 마구잡이로 사용하시는 분들이 많은듯 합니다. 하지만 C/C++의 캐스팅과 다르게 C#의 Boxing은 그 과정에서 개체 할당이 일어 나기 때문에 성능 이슈가 생길수 있습니다.
int num1 = 123; object obj = num1; // boxing int num2 = (int)obj; // unboxing
C#에서 사용되는 모든 타입들은 object 에서 직/간접적으로 상속을 받게 됩니다. 즉, 최상위 부모 클래스 같은 존재지요. 이것이 C/C++에서 였다면, 위의 num1에서 obj로의 대입은 단순 업캐스팅으로 변환되는 거기 때문에 큰 문제는 없을 겁니다. 하지만 C#에서는 다릅니다. C#에서는 이를 Boxing이라 부르며 아래와 같이 힙 Heap에 새로운 개체를 할당하고 그 곳에 값을 복사하게 됩니다.
C++ 코드로 보자면 아래와 같은 상황인겁니다.
int num1 = 123; int *obj = new int; (*obj) = num1;
이 때문에 MSDN에서도 Boxing, Unboxing에 대해 아래와 같이 조언하고 있습니다.
- System.Collections.ArrayList 같은 제네릭이 아닌 컬렉션 클래스의 예와 같이 많은 수의 boxing이 필요한 경우에는 값 형식을 사용하지 않는 것이 좋습니다. System.Collections.Generic.List
같은 제네릭 컬렉션을 사용하면 값 형식의 boxing을 방지할 수 있습니다. boxing 및 unboxing 과정에는 많은 처리 작업이 필요합니다. 값 형식을 boxing할 때는 완전히 새로운 개체가 만들어져야 하며, 이러한 작업은 간단한 참조 할당보다 최대 20배의 시간이 걸립니다. unboxing을 할 때는 캐스팅 과정에 할당 작업보다 4배의 시간이 걸릴 수 있습니다.
반대로 Unboxing의 경우 Boxing과는 다르게 대입하려는 타입을 명시적으로 지정해주어야 합니다. Bxoing 되어있는 타입과 명시한 타입이 같으면, 해당 객체의 값을 복사합니다.
C#은 C/C++에 비해 타입에 엄격하기 때문에 Boxing 타입과 다른 타입으로 Unboxing을 시도하면 InvalidCastException 같은 에러를 냅니다.