Enum은 의미 있는 상수 집합을 타입으로 표현해 코드 가독성과 안전성을 높여줍니다. 기본 사용부터 [Flags] 조합, 파싱, 검증, 성능 포인트까지 실용적으로 정리합니다.
1. 기본 개념과 선언
Enum은 정수 기반의 열거형입니다. 0을 None/Unknown으로 두면 기본값 처리에 유리합니다.
public enum Status
{
None = 0,
Pending = 1,
Active = 2,
Closed = 3
}
// 사용 예
Status s = Status.Pending;
if (s == Status.Active)
{
Console.WriteLine("활성 상태입니다.");
}
2. 기본 형식(Underlying Type) 지정
기본 형식은 int이며, 저장 공간이나 비트 마스크 용도에 따라 byte/short/long 등으로 변경할 수 있습니다.
public enum ErrorCode : short
{
None = 0,
Minor = 1,
Major = 2
}
public enum Color8 : byte
{
None = 0,
Red = 1,
Green = 2,
Blue = 3
}
3. [Flags]로 비트 조합 다루기
비트 플래그는 조합 가능한 옵션을 표현할 때 사용합니다. 2의 거듭제곱으로 정의하고 All을 제공하면 검증이 쉬워집니다.
[Flags]
public enum Permissions : byte
{
None = 0,
Read = 1 << 0,
Write = 1 << 1,
Execute = 1 << 2,
All = Read | Write | Execute
}
var p = Permissions.Read | Permissions.Write; // 결합
bool canWrite = (p & Permissions.Write) != 0; // 포함 여부(빠름)
// HasFlag는 간단하지만 상대적으로 느립니다: p.HasFlag(Permissions.Write)
p |= Permissions.Execute; // 추가
p &= ~Permissions.Read; // 제거
p ^= Permissions.Write; // 토글
// 유효성 검사: 정의되지 않은 비트가 있는지 확인
bool valid = (p & ~Permissions.All) == 0;
4. 문자열/숫자 변환과 파싱
사용자 입력이나 설정 값을 Enum으로 안전하게 변환합니다. 플래그는 "Read, Write" 형태도 허용됩니다.
// 문자열 -> Enum (대소문자 무시)
if (Enum.TryParse<Status>("Active", ignoreCase: true, out var status))
{
Console.WriteLine(status); // Active
}
// 플래그 파싱
Enum.TryParse<Permissions>("Read, Write", out var perms);
// Enum -> 문자열/숫자 포맷
var v = Status.Active;
Console.WriteLine(v.ToString()); // Active
Console.WriteLine(v.ToString("D")); // 2 (십진)
Console.WriteLine(v.ToString("X")); // 00000002 (16진)
var flags = Permissions.Read | Permissions.Write;
Console.WriteLine(flags.ToString("F")); // Read, Write (Flags 서식)
// 숫자 캐스팅
int n = (int)Status.Closed; // 3
5. 안전한 사용과 검증
정수 -> Enum 캐스팅은 항상 성공하지만 값이 정의됐다는 보장은 없습니다. 입력 검증을 습관화합니다.
int raw = 42;
// 위험: 미정의 값이 들어올 수 있음
var s1 = (Status)raw;
// 안전: 정의 여부 확인 (일반적으로 느릴 수 있음)
if (Enum.IsDefined(typeof(Status), raw))
{
var s2 = (Status)raw;
}
// 범위가 연속적이면 더 빠른 검증도 가능
bool inRange = raw >= (int)Status.None && raw <= (int)Status.Closed;
// Flags 유효성: 정의되지 않은 비트 제거/검증
Permissions fromInput = (Permissions)raw;
bool flagsValid = (fromInput & ~Permissions.All) == 0;
6. switch와 패턴 매칭
switch 식으로 가독성 높은 매핑을 만들 수 있습니다. 기본값은 로깅/폴백 처리로 남겨둡니다.
static string ToTitle(Status s) => s switch
{
Status.None => "없음",
Status.Pending => "대기",
Status.Active => "활성",
Status.Closed => "종료",
_ => $"알 수 없음({(int)s})"
};
// Flags에 when 가드 사용
static void Describe(Permissions p)
{
switch (p)
{
case var _ when (p & Permissions.All) == Permissions.All:
Console.WriteLine("모든 권한");
break;
case var _ when (p & Permissions.Write) != 0:
Console.WriteLine("쓰기 가능");
break;
case var _ when p == Permissions.None:
Console.WriteLine("권한 없음");
break;
default:
Console.WriteLine(p);
break;
}
}
7. 반복과 유틸리티
Enum 멤버를 순회하거나, 범용 파서/도우미를 만들어 두면 재사용성이 높아집니다.
// 모든 값 순회 (.NET Core 3.0+)
foreach (Status st in Enum.GetValues<Status>())
{
Console.WriteLine($"{st} = {(int)st}");
}
// 이름만 필요할 때
foreach (var name in Enum.GetNames<Status>())
{
Console.WriteLine(name);
}
// 범용 파서: 실패 시 기본값 반환
public static TEnum ParseOrDefault<TEnum>(string? text, TEnum @default)
where TEnum : struct, Enum
{
return Enum.TryParse<TEnum>(text, ignoreCase: true, out var value)
? value
: @default;
}
8. 베스트 프랙티스 체크리스트
- 0은 반드시 None/Unknown으로 예약합니다(기본값 안전성).
- 비즈니스 의미가 바뀌지 않도록 값을 명시적으로 할당합니다.
- [Flags]는 2의 거듭제곱과 All 집합을 제공합니다.
- 외부 입력은 TryParse, 범위/비트 마스크로 검증합니다.
- 빈번한 포함 체크는 HasFlag 대신 비트 연산을 사용합니다.
- 새 멤버 추가 시 기본 분기(default)를 통해 하위 호환을 유지합니다.
Enum을 잘 설계하면 매직 넘버를 제거하고, 유지보수와 버그 방지에 큰 도움을 받을 수 있습니다.
'C#' 카테고리의 다른 글
| C# 애트리뷰트 (Attribute) 정의와 활용 (0) | 2026.04.10 |
|---|---|
| C# StringBuilder로 문자열 성능 최적화 (0) | 2026.04.10 |
| C# var와 타입 추론 (0) | 2026.04.09 |
| C# 람다 식 (Lambda Expression) (1) | 2026.04.09 |
| C# using 문과 IDisposable (0) | 2026.04.08 |