코드에 적히지 않은 약속은 없는 약속이다
Esc

    코드에 적히지 않은 약속은 없는 약속이다

    –C#기초

    앞 글에서 이런 코드를 썼습니다.

    switch (day)
    {
        case 4:
            Console.WriteLine("목요일");
            break;
    }

    돌아가는 데는 문제가 없습니다. 문제는 반년 뒤입니다.

    day 에 들어 있는 4 가 목요일이라는 약속은 지금 코드 어디에도 없습니다. 사람 머릿속에만 있습니다. 한 주가 일요일부터인지 월요일부터인지, 0부터 세는지 1부터 세는지도 코드를 아무리 봐도 안 나옵니다.

    뜻이 있는 값에는 이름을 붙입니다. 그래야 약속이 코드 안에 남습니다.

    const 는 이렇게 생겼다

    바뀌지 않는 값 하나에 이름을 붙이는 문법입니다.

    const double Pi = 3.14159;
    
    Console.WriteLine(Pi * r * r);

    const 를 붙이면 두 가지가 강제됩니다.

    const int MaxHp;        // 컴파일 에러: 선언할 때 값을 줘야 합니다
    
    const int MaxHp = 100;
    MaxHp = 200;            // 컴파일 에러: 나중에 바꿀 수 없습니다

    앞에서 선언은 컴파일 시점, 할당은 실행 시점이라고 했습니다. const 는 그 둘이 컴파일 시점 한 번으로 합쳐진 것입니다. 그래서 컴파일할 때 이미 알 수 있는 값만 들어갈 수 있습니다. 실행해 봐야 아는 값, 예를 들어 사용자가 입력한 숫자는 const 가 될 수 없습니다.

    숫자를 그대로 쓰면 무슨 일이 생기나

    if (hp <= 30)
    {
        ShowWarning();
    }
    
    if (damage > 30)
    {
        PlayHitEffect();
    }

    두 개의 30 은 뜻이 다릅니다. 하나는 경고를 띄우는 체력 기준이고, 하나는 큰 피해를 판정하는 기준입니다. 코드에는 똑같이 30 이라고만 적혀 있습니다.

    경고 기준을 40으로 올리라는 요청이 오면, 파일에서 30 을 찾아 어느 쪽이 체력인지 하나씩 읽어 보고 고쳐야 합니다. 하나를 잘못 고치면 경고는 그대로고 타격 효과만 조용히 바뀝니다.

    const int WarningHp = 30;
    const int BigDamage = 30;

    이름을 붙이면 찾을 것이 없습니다. 고칠 자리가 한 곳이고, 그 한 줄만 바꾸면 됩니다.

    enum 은 이렇게 생겼다

    const 는 값 하나에 이름을 붙입니다. 그런데 요일처럼 정해진 선택지가 묶음으로 있는 경우가 있습니다. 이때 쓰는 것이 enum 입니다.

    enum Days { Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday }
    
    Days today = Days.Monday;
    
    Console.WriteLine(today);        // Monday

    이름만 나열했는데 값은 어디서 나오느냐면, 컴파일러가 0부터 붙여 줍니다.

    0 2 3 4 5 6 1 Sunday Tuesday Wednesday Thursday Friday Saturday Monday 적은 순서대로 0부터 번호가 붙는다
    Days.Monday 는 사실 1이다.

    번호가 마음에 안 들면 직접 적어도 됩니다.

    enum Days { Sunday = 1, Monday, Tuesday }   // 1, 2, 3

    하나만 적으면 그다음부터는 이어서 붙습니다.

    enum 과 switch 는 짝이다

    이제 앞 글의 코드를 다시 씁니다.

    switch (today)
    {
        case Days.Monday:
            Console.WriteLine("월요일");
            break;
        case Days.Thursday:
            Console.WriteLine("목요일");
            break;
    }

    case 4: 였을 때와 하는 일이 똑같습니다. 달라진 것은 읽는 사람입니다. 주석도 문서도 없이 코드만 보고 뜻을 압니다.

    enum 은 후보가 정해져 있고 switch 는 후보를 늘어놓는 문법이라 서로 잘 맞습니다. Days 를 하나 늘리면 switch 에 case 를 하나 더 적으면 되고, 그 자리를 빠뜨렸는지도 눈으로 셀 수 있습니다.

    enum 도 결국 정수다

    여기서 한 가지 짚고 넘어가야 합니다. enum 은 새로운 종류의 값이 아니라 정수에 이름을 씌운 것입니다. 바탕에 깔린 값은 그대로 int 입니다.

    Console.WriteLine((int)Days.Thursday);   // 4

    그래서 이런 것도 컴파일이 됩니다.

    Days today = (Days)99;

    Days 에 99번은 없습니다. 그래도 에러가 나지 않습니다. 앞에서 본 대로 캐스팅은 “내가 책임진다”는 표시이고, 컴파일러는 여기서 값의 범위를 확인해 주지 않습니다.

    앞 글에서 default 를 적어 두라고 한 이유가 여기입니다. enum 을 쓰면 후보가 다 막힌 것처럼 보이지만, 실제로 막힌 것은 실수로 다른 값을 적는 일이지 다른 값이 들어오는 일이 아닙니다.

    정리

    • 뜻이 있는 값을 숫자 그대로 쓰면, 그 뜻은 코드 밖에만 남습니다.
    • const 는 값 하나에 이름을 붙입니다. 선언할 때 값을 줘야 하고 나중에 못 바꿉니다.
    • const 에는 컴파일할 때 이미 아는 값만 들어갑니다.
    • enum 은 정해진 선택지 묶음에 이름을 붙입니다. 적은 순서대로 0부터 번호가 붙습니다.
    • enum 과 switch 는 짝입니다. 후보가 정해져 있다는 성질이 같습니다.
    • enum 의 바탕은 정수입니다. 캐스팅으로 후보 밖의 값도 만들어지므로 default 는 남겨 둡니다.