비관리형 형식

비관리형 형식 (Unmanaged types)

어떤 형식이 "자기가 담고 있는 데이터가 전부 메모리 어딘가에 그대로 놓여 있고, 그 크기를 컴파일 타임에 정확히 알 수 있는가"를 기준으로 분류하는 게 있어요. C#에서는 그런 형식들을 비관리형 형식(unmanaged type) 이라고 부르고, 이 기준이 코드에서 어떤 역할을 하는지 하나씩 살펴볼게요.

출처: Microsoft Learn

본문

어떤 형식이 아래 목록 중 하나에 해당하면 비관리형 형식이라고 해요.

  • sbyte, byte, short, ushort, int, uint, long, ulong, nint, nuint, char, float, double, decimal, bool
  • 어떤 enum 형식이든
  • 어떤 포인터 형식이든
  • 구멍을 이루는 모든 멤버가 전부 비관리형 형식인 튜플
  • 비관리형 형식의 필드만 담은 사용자 정의 struct 형식

쭉 보면 공통점이 보이죠. 원시 타입들은 말할 것도 없고, enum이나 포인터, 그리고 "안에 든 것들이 전부 비관리형"인 튜플과 struct까지 포함돼요. 핵심은 "필드들을 전부 비관리형으로만 채웠는가"가 기준이라는 점이에요.

제네릭에서 "이 타입 매개변수는 포인터도 아니고 nullable도 아닌 비관리형이어야 한다"고 제약을 걸고 싶을 때는 unmanaged 제약 조건을 쓰면 돼요.

[!INCLUDEcsharp-version-note]

여기서 한 가지 재미있는 점이 있는데, constructed struct 형식(제네릭 struct에 실제 타입 인수를 채워 만든 형식)도 그 필드가 전부 비관리형이라면 역시 비관리형이 돼요. 아래 예시를 보면서 확인해볼게요.

using System;

public struct Coords<T>
{
    public T X;
    public T Y;
}

public class UnmanagedTypes
{
    public static void Main()
    {
        DisplaySize<Coords<int>>();
        DisplaySize<Coords<double>>();
    }

    private unsafe static void DisplaySize<T>() where T : unmanaged
    {
        Console.WriteLine($"{typeof(T)} is unmanaged and its size is {sizeof(T)} bytes");
    }
}
// Output:
// Coords`1[System.Int32] is unmanaged and its size is 8 bytes
// Coords`1[System.Double] is unmanaged and its size is 16 bytes

Coords<T>는 그 자체로 보면 unmanaged이기도 managed이기도 한 형식이에요. 실제로는 타입 인수가 뭐냐에 따라 갈리죠. Coords<int>Coords<double>처럼 필드가 비관리형으로 채워진 건 비관리형이고, Coords<object>object 형식의 필드를 갖기 때문에 managed 형식이 돼요. object 자체가 관리되는(managed) 형식이니까요.

만약 "내가 만든 제네릭 struct는 어떤 타입 인수가 와도 무조건 unmanaged여야 한다"고 강제하고 싶다면, struct 정의에 unmanaged 제약 조건을 붙이면 돼요.

public struct Coords<T> where T : unmanaged
{
    public T X;
    public T Y;
}

이렇게 where T : unmanaged를 붙여두면 Coords<T>T가 비관리형일 때만 성립하는 형식이 돼서, 모든 constructed 형식이 자연스럽게 비관리형으로 보장돼요.

C# 언어 사양

더 자세한 내용은 C# 언어 사양포인터 형식(Pointer types) 섹션을 참고하면 돼요.

더 알아보기