Study/C#

공용 타입 시스템

해달 2022. 2. 6. 00:00

개요

.NET에서는 여러 .NET 언어를 지원하기 위해 공용 타입 시스템(CTS; Common Type System)을 지원한다. .NET의 모든 형식은 값 타입(Value Type) 혹은 참조 타입(Reference Type)으로 구분되며 모든 타입은 기본 타입인 System.Object에서 파생된다. C#에서는 아래와 같은 계층을 가지고 있다.

 

공용 타입 시스템

값 타입

C#에서 값 타입은 구조체 / 열거형 / 그 외에 기본 제공 타입으로 구성된다. 값 타입은 아래와 같은 특징을 갖는다.

  • 구조체를 제외한 모든 타입은 System.ValueType에서 파생된다.
  • 스택 메모리에 직접 값이 포함된다. 다시 말해 복사가 일어난다.
  • 상속이 불가능하다.
  • 구조체 멤버 중에 참조 타입이 있다면 메모리 주소가 복사된다.*
    * 얕은 복사(Shallow Copy)라고 한다.

참조 타입

C#에서 참조 타입은 클래스 / 대리자 / 배열 / 인터페이스가 있다. 참조 타입은 아래와 같은 특징이 있다.

  • 힙 메모리에 인스턴스가 할당된다.
  • 참조 타입의 변수는 인스턴스의 주소에 대한 참조를 가진다.
  • 널(Null)을 할당할 수 있다.

박싱과 언박싱

박싱(Boxing)은 값 타입을 object 타입 또는 값 타입에서 구현된 임의의 인터페이스 타입으로 변환하는 프로세스며, 언박싱(Unboxing)은 박싱된 인스턴스에서 값 타입을 추출하는 프로세스다. 박싱이 일어날 땐 object 타입의 새로운 인스턴스를 생성해 값을 힙으로 복사하고, 언박싱이 이뤄질 땐 힙에서 스택으로 값이 복사된다. 또한, 박싱은 암시적으로 이뤄지며, 언박싱은 명시적으로 이뤄진다.

 

박싱과 언박싱은 많은 계산 과정을 필요로 하기 때문에 코드를 작성할 시 주의를 요구한다. 이를 실제로 확인하기 위해 아래와 같은 테스트 코드를 작성했다.

using System.Collections;

TimeSpan CalculateElapsedTime(Action action)
{
    DateTime start = DateTime.Now;
   
    action();

    return DateTime.Now - start;
}

ArrayList arrayList = new ArrayList();
TimeSpan withBoxing = CalculateElapsedTime(() =>
{
    for (int i = 0; i < 10000; ++i)
    {
        arrayList.Add(1);
    }
});

Console.WriteLine($"박싱이 일어나는 경우에 걸린 시간 : {withBoxing.Ticks * 10} us");

List<int> list = new List<int>();
TimeSpan withoutBoxing = CalculateElapsedTime(() =>
{
    for (int i = 0; i < 10000; ++i)
    {
        list.Add(1);
    }
});

Console.WriteLine($"박싱이 일어나지 않은 경우에 걸린 시간 : {withoutBoxing.Ticks * 10} us");

TimeSpan withUnboxing = CalculateElapsedTime(() =>
{
    for (int i = 0; i < arrayList.Count; ++i)
    {
        int element = (int)arrayList[i];
    }
});

Console.WriteLine($"언박싱이 일어나는 경우에 걸린 시간 : {withUnboxing.Ticks * 10} us");

TimeSpan withoutUnboxing = CalculateElapsedTime(() =>
{
    for (int i = 0; i < list.Count; ++i)
    {
        int element = list[i];
    }
});

Console.WriteLine($"언박싱이 일어나지 않은 경우에 걸린 시간 : {withoutUnboxing.Ticks * 10} us");

이 코드에 대한 결과는 아래와 같다.

 

 

박싱의 경우 약 30배 정도의 차이가 있으며, 언박싱은 약 1.1배의 차이가 있다. 언박싱의 경우 int가 아닌 구조체라면 더 걸릴 것이다. 따라서 박싱과 언박싱이 일어나지 않도록 주의하자.

참고자료



'Study > C#' 카테고리의 다른 글

가비지 컬렉션  (0) 2022.02.06
C#에 대한 이해  (0) 2022.02.05