본문 바로가기

Library/.NET Framework with C#

C#에서의 boxing과 unboxing

C#에서 boxing과 unboxing은, 포인터라는 존재가 없는 C#에서 어떻게 값(value)과 참조(reference) 타입을 다루는지 보여주는 중요한 개념이다. 이미 알고 있겠지만, C#에서는 포인터라는 개념이 존재하지 않으며, 사용자가 할당한 힙은 가비지 컬렉터(Garbage Collector)에 의해서 자동적으로 수거된다.

일반적으로, C/C++ 프로그래머들은 변수들이 자동 변수(auto variable)들은 스택 영역과 힙에 생성될 수 있다고 알고 있을 것이다. 즉, 스택에 생성되는 변수들에 대해서는 메모리 해제에 신경 쓸 필요가 없다. 그렇다면 C#에서는 어떠한가? C#에서도 변수들이 생성되는 위치에 차이가 있는가?

간단히 설명하면, MSIL에서, value 타입으로 선언된 변수들은 스택에 생성되며, reference 타입으로 생성되는 변수들은 힙에 생성된다. 그리고, 이 부분이 C++과 C#이 다른 부분인데, C++에서는 개체를 스택과 힙 어느 쪽에서든 생성할 수 있지만, C#에서는 개체는 new 키워드를 이용해서 힙에서만 생성된다. 예를 들어,

MyClass myclass;

라고 C++에서 선언한다면, myclass라는 인스턴스가 스택에 생성되겠지만, C#에서는 타입의 변수로서 MyClass가 선언된 것이다. C#에서 이것을 실제로 인스턴스화 하려면

MyClass myclass = new MyClass();

라고 해주어야 한다. 이 부분도 주의가 필요한 부분인데, new 뒤의 생성자를 반드시 명시적으로 지정해주어야 한다. C++에서는, new myclass만 하더라도 Type의 암시적 생성자가 호출되기 때문에 myclass()와 myclass의 차이는 없었지만, C#에서는 반드시 생성자를 ()를 붙여서 명시적으로 지정해주어야 한다.


즉, C/C++의 포인터처럼 어떤 값을 사용하고 싶다고 한다면 object라는, 모든 개체가 상속받는 최상위 슈퍼 클래스를 선언하고, 어떤 타입을 여기에 대입하고, 원하는 형태로 캐스팅해서 사용하면 된다. 여기서, 값(value)을 참조(reference) 타입으로 변환하는 것을 boxing이라 하고, 반대의 과정을 unboxing이라 한다. 언제 boxing과 unboxing이 일어나는지 파악하는 것은 매우 중요한데, 왜냐하면 컴파일러가 이 과정을 자동으로 최적화하지 않기 때문이다. 참조가 필요한 곳에 값의 형태로 인자를 넘겨준다면, 의미없는 boxing 과정이 추가된다. box OpCode Field는 5바이트 코드로, 의미없는 boxing의 남발은 성능상의 문제가 발생할 수 있기 때문이다. 이런 경우, 이미 참조 형식으로의 변수를 만들어 놓은 뒤 필요한 부분에서 이 참조 형태의 변수를 넘겨준다면, 의미없는 boxing을 제거할 수 있다.

타입 변환은 C#내에서, 더 자세하게 말하자면 MSIL 내에서 엄격하게 정의되어 있기 때문에, C 방식의 무차별 캐스팅에 비해서 훨씬 더 안전한 변환을 보장한다.