C#

C# 객체지향 문법 [C#의 클래스 확장 - 열거형]

devrabbit22 2025. 7. 23. 00:51

열거형(enumeration type)도 값 형식의 하나로 byte, sbyte, short, ushort, int, uint, long, ulong만을 상속받아 정의할 수 있는 제한된 사용자 정의 타입이다.

[접근_제한자] enum 타입명
{
    // 숫자를 대표하는 식별자 이름 나열
}
// enum 타입은 숫자형 값에 사람이 인식하기 쉬운 문자열 이름을 부여한다. 
// 상속 타입을 지정하지 않는 경우 기본적으로 System.Int32가 된다.

enum 타입의 사용

  • enum은 내부에 정의된 식별자 순서에 따라 각각 0부터 시작해 1씩 값을 증가시키며 대응시킨다.
  • 따라서 예제 코드의 Sunday는 숫자0이고, 그 이후로 1, 2, 3, ...과 같은 식으로 증가해 Saturday는 6이 되어 결국 상속받은 System.Int32 타입에 해당하는 값이 된다.
  • enum 변수를 출력했을 때 숫자 0이 아닌 Sunday라는 문자열이 출력된 이유?
  • enum의 조상이 System.Object임을 감안하면 enum은 ToString 메서드를 재정의햇고, 그것의 내부 코드에서 숫자값보다는 문자열로 반환하는 역할을 하기 때문이다.
  • System.Int32를 부모로 두기 때문에 Days 타입은 int를 비롯해 각종 숫자형 타입과 형 변환하는 것이 가능하고 그 반대도 가능하다.
  • 제약이라면 암시적 형 변환이 아닌 명시적 형 변환을 해야한다는 것이다.

enum의 시작 요소 값에 0이 아닌 다른 정수를 지정할 수도 있다.

그 이후의 요소에 대해서도 1씩 자동으로 증가하는 것이 아닌 개발자가 임의의 값을 지정할 수도 있다.

enum Days
{
    Sunday = 5, Monday = 10, Tuesday, Wednesday, Thursday = 15, Friday, Saturday
}

위의 경우 각 요소별로 5, 10, 11, 12, 15, 16, 17로 값이 할당된다. 

개발자가 값을 할당할 때 주의할 점

  • enum이 상속받은 부모의 숫자 타입 범위에 있는 값을 지정해야 한다.
  • ex) Days 타입은 int가 부모이므로 long 형에 해당하는 값을 지정하면 컴파일할 때 오류가 발생한다.
  • 추가로 enum을 값의 조합으로 사용하는 것도 가능하다.
  • ex) Days 인트턴스가 '작업일'이라는 것을 나타내기 위해 Monday 부터 Friday까지의 값을 담을 수 있다.
  • 이때 각 요소에 대한 값을 2의 배수로 지정한다.
enum Days
{
    Sunday = 1, Moday = 2, Tuesday = 4,
    Wednesday = 8, Thursday = 16, Friday = 32, Saturday = 64
}

이렇게 정의하면 다음과 같이 |(OR) 연산자를 사용해 정수형 값을 겹칠 수 있고, HasFlag 메서드를 사용해 특정 요소 값을 포함하고 있는지도 판단할 수 있다.

  • 마지막 출력이 62가 나오는데 정수 연산으로 보면 맞는 결과지만, 개별 정수가 의미 있는 식별자로 묶는 것이 enum 타입이라는 점을 감안하면 오히려 출력 값은 'Monday, Tuesday, Wednesday, Thursday, Friday'가 되야 맞다.
  • 이런 식으로 enum 타입의 인스턴스가 여러 개의 값을 포함하는 용도로 사용된다는 것을 알리기 위해 [Flags] 특성을 지정할 수 있다.

[Flags] 특성은 enum타입에만 사용될 수 있고 다음과 같이 타입 정의를 할때 함께 지정하면 된다.

  • 이렇게 변경하고 다시 실행하면 62가 아닌 문자열이 workingDays의 출력 값으로 나타난다.
  • enum을 적절하게 사용하면 코드의 가독성 및 오류를 줄일 수 있다.
  • ex) 사칙 연산을 수행하는 메서드를 만든다고 가정하면, enum이 없을 경우 아래와 같은 식으로 메서드를 정의할 것이다.
int Calc(char opType, int operand1, int operand2)
{
    switch(opType)
    {
        case '+' : return operand1 + operand2;
        case '-' : return operand1 - operand2;
        case '*' : return operand1 * operand2;
        case '/' : return operand1 / operand2;
    }
    return 0;
}

결과적으로 위의 코드는 동작에는 문제가 없지만 향후 유지보수가 번거롭게된다.

  • Calc 메서드를 사용하는 측에서 Calc 메서드에서 어떤 연산을 제공하는지 알 수 없으므로 반드시 Calc 내부의 코드를 살펴보거나 제공되는 도움말을 참조해야 한다.
  • 또한 개발자가 실수로 오타라도 내면 프로그램이 실행될 때 정상적인 연산이 수행되지 않을 수 있고, 나중에 나머지(%:Remainder)연산을 추가하는 경우 Calc 개발자는 이러한 사실을 반드시 다른 개발자에게 알려야 한다. 

이와 같은 다양한 문제를 enum으로 쉽게 해결할 수 있다.

 int Calc(CalcType opType, int operand1, int operand2)
 {
     switch (opType)
     {
         case CalcType.Add: return operand1 + operand2;
         case CalcType.Minus: return operand1 - operand2;
         case CalcType.Multiply: return operand1 * operand2;
         case CalcType.Divide: return operand1 / operand2;
     }
     return 0;
 }

 Calc(CalcType.Add, 5, 6);
  • enum을 사용하면 개발자는 CalcType enum 정의만 봐도 지원되는 연산을 짐작할 수 있고, 오타가 발생하더라도 컴파일러가 오류를 발생시킬 것이므로 그에 대한 걱정도 할 필요가 없다.
  • 향후 연산이 추가되더라도 CalcType을 참조하는 어떤 개발자라도 쉽게 추가된 사실을 인지할 수 있다.

Reference

시작하세요! C# 12 프로그래밍 기본 문법부터 실전 예제까지