C# 2.8 · 2부. 객체지향

열거형

정해진 값 몇 가지에 이름을 붙입니다.

예상 학습 시간 14분 실행 단추가 있는 예제는 고쳐서 실행해 볼 수 있습니다 난이도 기초
개념 설명

정해진 값 몇 가지에 이름 붙이기

주문 상태를 0·1·2 로 두거나 "대기"·"승인" 같은 문자열로 다루면, 코드 어디에서도 어떤 값이 올 수 있는지 알 수 없습니다. 3 을 담아도, "승인됨" 이라고 오타를 내도 컴파일러는 아무 말이 없습니다.

열거형(enum)올 수 있는 값을 미리 늘어놓고 이름을 붙인 형식입니다. 그 목록에 없는 값은 담을 수 없으므로, 잘못이 실행 전에 드러납니다.

  • 값마다 이름이 있어 코드를 읽는 것만으로 뜻이 통합니다.
  • 목록에 없는 것은 담을 수 없습니다. 오타는 컴파일 오류입니다.
  • 밑에는 정수가 깔려 있어 저장하거나 주고받기 좋습니다.
최소 예제 · 편집 가능

이름 붙은 값 다루기

아래 코드는 브라우저 안에서 실제로 실행됩니다. 고쳐서 눌러 보셔도 됩니다.

Program.cs
var status = Status.Approved;

Console.WriteLine(status);
Console.WriteLine((int)status);

if (status == Status.Approved) {
    Console.WriteLine("승인되었습니다");
}

enum Status {
    Pending,     // 0 — 적지 않으면 0부터 1씩 올라갑니다
    Approved,    // 1
    Rejected     // 2
}
출력
Approved 1 승인되었습니다

코드는 고쳐서 실행해 볼 수 있습니다. 처음 누를 때만 실행기를 내려받느라 잠시 걸립니다. 적은 코드는 서버로 나가지 않습니다.

화면에 찍으면 숫자가 아니라 이름이 나옵니다. 밑에 깔린 정수가 필요하면 (int) 로 꺼내면 됩니다 — 1.4에서 본 캐스트가 여기서도 그대로 쓰입니다.

상세 사용법

무엇이 나아지는가

문자열이나 숫자로 두면
string status = "승인";

// 오타가 그냥 지나갑니다
if (status == "승인됨") { }

// 없는 값도 담깁니다
status = "아무거나";

// 무엇이 올 수 있는지
// 코드가 말해 주지 않습니다
열거형으로 두면
Status status = Status.Approved;

// 오타는 컴파일 오류
if (status == Status.Approvd) { }

// 없는 이름도 컴파일 오류
status = Status.Whatever;

// 무엇이 올 수 있는지
// 형식이 알려 줍니다

값을 직접 정하기

적지 않으면 0부터 1씩 올라가지만, 필요하면 직접 정할 수 있습니다. 데이터베이스에 이미 정해진 코드가 있거나, 나중에 항목을 사이에 끼워 넣어도 저장된 숫자가 달라지지 않게 하려면 적어 두는 편이 안전합니다.

enum Level {
    Guest = 10,
    Member = 20,
    Admin = 30
}

// 밑바탕 형식도 정할 수 있습니다(기본은 int)
enum Small : byte { A, B }

첫 항목은 0이라는 점을 기억해두세요. 열거형의 기본값은 0이므로, 값을 담지 않은 변수나 객체의 필드는 첫 항목이 됩니다. 그래서 첫 자리에 None 이나 Unknown 처럼 "아직 정해지지 않음" 을 두는 것이 널리 쓰이는 관례입니다.

switch 와 함께

열거형은 올 수 있는 값이 정해져 있으므로 1.5의 switch 와 잘 어울립니다. 항목을 빠뜨리면 편집기가 알려 주기도 합니다.

string message = status switch {
    Status.Pending => "기다리는 중입니다",
    Status.Approved => "승인되었습니다",
    Status.Rejected => "반려되었습니다",
    _ => "알 수 없습니다"
};

_ 를 빼지 마세요. 열거형은 정수라서 목록에 없는 값도 담길 수 있습니다 — 밖에서 받은 숫자를 캐스트해 넣으면 (Status)99 같은 것이 들어옵니다. 컴파일러는 막지 않으므로 마지막 자리를 비워 두면 실행 중에 예외가 납니다.

var weird = (Status)99;         // 컴파일됩니다
Console.WriteLine(weird);              // 99 — 이름이 없어 숫자가 나옵니다
Console.WriteLine(Enum.IsDefined(weird));   // False

여러 개를 함께 담기

권한처럼 여럿을 동시에 가질 수 있는 것은 항목마다 겹치지 않는 비트를 주고 [Flags] 를 붙입니다. 1.4에서 본 비트 연산자가 여기서 제 몫을 합니다.

[Flags]
enum Permission {
    None  = 0,
    Read  = 1,      // 0001
    Write = 2,      // 0010
    Share = 4       // 0100
}

// | 로 합치고 HasFlag 로 물어봅니다
var mine = Permission.Read | Permission.Write;

Console.WriteLine(mine);                              // Read, Write
Console.WriteLine(mine.HasFlag(Permission.Write));   // True
Console.WriteLine(mine.HasFlag(Permission.Share));   // False

값은 반드시 1, 2, 4, 8… 처럼 자리마다 하나씩 켜지게 주어야 합니다. 3을 주면 이미 Read | Write 와 같아져 섞입니다. [Flags] 를 붙이면 화면에 찍을 때도 합쳐진 이름을 알아보기 좋게 내줍니다.

버전 배지

이 문법, 몇 버전부터 사용할 수 있나요

1.0
2.0
3.0
6.0
7.0
8.0
9.0
12.0
14.0
  • 1.0enum · [Flags] · 밑바탕 형식 지정
  • 8.0switch 식으로 항목 분기하기
C# 1.0+

열거형은 C# 1.0부터 있었고 문법이 거의 바뀌지 않았습니다. 달라진 것은 다루는 방법입니다 — C# 8.0의 switch 식이 항목마다 값을 고르는 코드를 크게 줄였고, 라이브러리 쪽에서도 Enum.TryParse 나 제네릭 Enum.IsDefined 처럼 안전하게 다루는 도구가 더해졌습니다.

실습 문제

직접 해보기

1. 요일에 이름 붙이기 난이도 하

요일을 담는 Day 열거형을 만들고, 받은 요일이 주말인지 평일인지 출력해보세요. 1.5의 switch 식을 사용하면 짧게 적을 수 있습니다.

여러 항목을 하나로 묶을 때는 Day.Sat or Day.Sun 처럼 적습니다(C# 9.0+). 마지막에 _ 를 두는 것을 잊지 마세요.
var today = Day.Sat; string kind = today switch { Day.Sat or Day.Sun => "주말", _ => "평일" }; Console.WriteLine(kind); // 주말 enum Day { Mon, Tue, Wed, Thu, Fri, Sat, Sun }
2. 권한 겹쳐 담기 난이도 중

읽기·쓰기·지우기를 담는 Access 열거형을 [Flags] 로 만들고, 읽기와 쓰기를 함께 가진 값을 만들어 지우기 권한이 있는지 물어보세요.

값은 1, 2, 4 처럼 자리마다 하나씩 켜지게 줍니다. 합칠 때는 |, 물어볼 때는 HasFlag 입니다.
var mine = Access.Read | Access.Write; Console.WriteLine(mine); // Read, Write Console.WriteLine(mine.HasFlag(Access.Delete)); // False // 나중에 더할 때도 | 로 얹습니다 mine = mine | Access.Delete; Console.WriteLine(mine.HasFlag(Access.Delete)); // True [Flags] enum Access { None = 0, Read = 1, Write = 2, Delete = 4 }
요약
  • 열거형은 올 수 있는 값을 미리 늘어놓고 이름을 붙인 형식입니다.
  • 첫 항목은 0이자 기본값입니다. 그 자리에 None 을 두는 것이 관례입니다.
  • 밑에 깔린 것은 정수라 목록에 없는 값도 담길 수 있습니다. switch 식에 _ 를 꼭 두세요.
  • 여럿을 함께 가지려면 [Flags]1·2·4·8… 입니다.

C# 버전별 변경 이력 에서 각 버전이 무엇을 더했는지 볼 수 있습니다.