Programing/C#

C# 프로그래밍의 기초: Boxing과 Unboxing

유니얼 2024. 10. 12. 21:25
728x90

C#에서 Boxing과 Unboxing은 값 타입과 참조 타입 간의 변환을 다루는 중요한 개념입니다. 이 두 과정은 C#의 데이터 형식 처리 방식에서 발생하는 성능 이슈와 메모리 관리를 이해하는 데 필수적인 요소입니다. 이번 블로그 글에서는 Boxing과 Unboxing의 개념을 설명하고, 실제로 어떻게 동작하는지 예제를 통해 알아보겠습니다.

Boxing이란?

Boxing은 값 타입(Value Type)을 참조 타입(Reference Type)으로 변환하는 과정입니다. C#에서 모든 값 타입(예: int, bool, char)은 스택에 저장되지만, 참조 타입은 힙에 저장됩니다. 값 타입을 참조 타입으로 변환할 때, 값은 힙에 저장되고, 그 값을 가리키는 참조가 생성됩니다. 이 과정을 Boxing이라고 합니다.

Boxing의 과정

  • 값 타입의 값을 힙 영역에 할당된 객체에 넣고, 해당 객체의 참조를 반환합니다.
  • 이 과정에서 스택에 있던 값이 힙으로 이동하고, 참조가 스택에 남습니다.

Boxing 예제

int number = 123;
object obj = number; // Boxing 발생

 

위 코드에서 number는 값 타입인 int이고, obj는 참조 타입인 object입니다. number 값을 obj로 변환할 때 Boxing이 발생하며, number의 값이 힙 영역에 복사되고, obj는 그 값을 참조하게 됩니다.

Unboxing이란?

UnboxingBoxing된 참조 타입을 다시 값 타입으로 변환하는 과정입니다. Boxing과 반대로, 힙에 저장된 값 타입을 다시 스택으로 복사하여 값을 복원하는 작업입니다. Unboxing은 명시적으로 수행해야 하며, Boxing된 참조를 올바른 값 타입으로 변환하지 않으면 오류가 발생할 수 있습니다.

Unboxing의 과정

  • 참조 타입으로 Boxing된 값을 다시 값 타입으로 변환하여 스택에 복사합니다.
  • 변환할 타입이 정확히 맞지 않으면 InvalidCastException 예외가 발생할 수 있습니다.

Unboxing 예제

object obj = 123; // Boxing
int number = (int)obj; // Unboxing

 

위 예제에서 obj는 Boxing된 int 값입니다. 이를 다시 int로 변환할 때, 명시적인 캐스팅 (int)가 필요하며, 이때 Unboxing이 발생합니다.

Boxing과 Unboxing의 성능 문제

Boxing과 Unboxing은 간단해 보이지만, 성능 상의 이슈가 발생할 수 있습니다. 특히, Boxing은 힙 메모리 할당과 GC(Garbage Collection)의 부담을 초래할 수 있으며, Unboxing은 Boxing된 값을 다시 스택으로 복사하는 비용이 발생합니다. 이러한 변환이 빈번하게 발생할 경우 성능 저하를 유발할 수 있으므로 주의가 필요합니다.

성능 문제를 줄이는 방법

  • 값 타입을 필요 이상으로 Boxing하지 않도록 주의합니다.
  • ArrayList와 같은 컬렉션 대신 List<T> 같은 제네릭 컬렉션을 사용하면 Boxing을 방지할 수 있습니다.
// Boxing 발생
ArrayList arrayList = new ArrayList();
arrayList.Add(123); // int가 object로 Boxing됨

// Boxing 방지
List<int> list = new List<int>();
list.Add(123); // 값 타입 그대로 처리됨

Boxing과 Unboxing의 실전 예제

using System;

namespace BoxingUnboxingExample
{
    class Program
    {
        static void Main(string[] args)
        {
            // Boxing 예시
            int num = 10;
            object obj = num; // Boxing 발생
            Console.WriteLine($"Boxing: num = {num}, obj = {obj}");

            // Unboxing 예시
            object obj2 = 20;
            int num2 = (int)obj2; // Unboxing 발생
            Console.WriteLine($"Unboxing: obj2 = {obj2}, num2 = {num2}");

            // Boxing/Unboxing 성능 테스트
            int sum = 0;
            object boxedNum;
            for (int i = 0; i < 1000000; i++)
            {
                boxedNum = i; // Boxing 발생
                sum += (int)boxedNum; // Unboxing 발생
            }
            Console.WriteLine($"Final sum: {sum}");
        }
    }
}

 

위 예제는 Boxing과 Unboxing의 기본적인 동작 원리를 보여줍니다. num 값이 object로 Boxing되고, 이후에 obj2 값이 다시 int로 Unboxing됩니다. 마지막 루프에서는 Boxing과 Unboxing이 빈번하게 발생하며, 이런 경우 성능 상의 부담이 클 수 있음을 보여줍니다.

결론

Boxing과 Unboxing은 값 타입과 참조 타입 간의 변환을 처리하는 C#의 중요한 개념입니다. Boxing은 값 타입을 참조 타입으로 변환하여 힙에 저장하고, Unboxing은 이를 다시 값 타입으로 변환합니다. 그러나 이러한 변환 과정은 성능에 부정적인 영향을 줄 수 있으므로, 불필요한 Boxing과 Unboxing을 피하는 것이 중요합니다. 제네릭 컬렉션이나 값 타입을 최대한 값으로 처리하는 방식으로 성능 문제를 줄일 수 있습니다.

 

값 타입과 참조 타입 간의 상호 변환을 이해하고, 적절히 사용하는 것이 C# 프로그래밍의 효율성을 높이는 데 중요한 역할을 합니다.

반응형