[c#] 1.0이 아닌 열거 형 값에 0.0을 할당 할 수있는 이유

호기심에서 : 왜 1.0이 아닌 열거 형인 변수에 0.0을 할당 할 수 있습니까? 다음 코드를 살펴보십시오.

public enum Foo
{
    Bar,
    Baz
}

class Program
{
    static void Main()
    {
        Foo value1 = 0.0;
        Foo value2 = 1.0;   // This line does not compile
        Foo value3 = 4.2;   // This line does not compile
    }
}

숫자 유형과 열거 값 간의 변환은 캐스트를 통해서만 허용된다고 생각했습니다. 즉, Foo를 작성 value2 = (Foo) 1.0;하여 두 번째 줄 Main이 컴파일 될 수 있습니다. 0.0C # 의 값 에 예외가있는 이유는 무엇 입니까?



답변

0.0을 사용할 수있는 버그입니다. 컴파일러는 값이 0 인 모든 상수 식을 암시 적으로 0으로 처리합니다.

이제 컴파일러가 C # 5 사양의 섹션 6.1.3에 따라 0 의 상수 식에서 열거 형으로 의 암시 적 변환을 허용하는 것이 습니다 int.

암시 적 열거 변환은 decimal-integer-literal 0이 모든 열거 형 및 기본 유형이 열거 형인 모든 nullable-type으로 변환되도록 허용합니다. 후자의 경우 변환은 기본 열거 형 형식으로 변환하고 결과를 래핑하여 평가됩니다 (§4.1.10).

이전에 C # 팀과 이야기를 나눴습니다. 우연히 0.0 (실제로 0.0m 및 0.0f)에서 열거 형 값으로 변환 하는 것을 제거하고 싶었지만 불행히도 코드가 너무 많이 손상 되었음에도 불구하고 애초에 허용되어서는 안됩니다.

모노 mcs는 있지만 컴파일러는,이 부동 소수점 변환을 모두 금지 않습니다 수 있습니다 :

const int Zero = 0;
...

SomeEnum x = Zero;

그것이 Zero상수 표현이지만 십진 정수 리터럴이 아니라는 사실에도 불구하고 .

값이 0 인 정수 상수 표현식 (예 : 모방 mcs) 을 허용하도록 앞으로 C # 사양이 변경되는 것을보고 놀라지 않을 것입니다 . 그러나 부동 소수점 변환이 공식적 으로 정확할 것이라고 기대하지는 않습니다 . (물론 C #의 미래를 예측하는 것에 대해 이전에 틀 렸습니다 …)


답변

Jon의 대답이 맞습니다. 나는 그것에 다음과 같은 점을 추가 할 것이다.

  • 이 어리 석고 부끄러운 버그를 일으켰습니다. 많은 사과드립니다.

  • 이 버그는 컴파일러에서 “expression is zero”술어의 의미를 오해하여 발생했습니다. 실제로 “이 유형의 기본값입니까?”라는 줄을 따라 더 많은 것을 검사 할 때 정수 0이 같음 만 검사한다고 믿었습니다. 사실, 이전 버전의 버그에서는 실제로 모든 유형의 기본값을 열거 형에 할당 할 수있었습니다! 이제는 숫자의 기본값 일뿐입니다. (수업 : 헬퍼 술어의 이름을 신중하게 지정하십시오.)

  • 내가 엉망으로 구현하려고 시도한 동작은 사실 약간 다른 버그에 대한 해결 방법이었습니다. https://docs.microsoft.com/en-us/archive/blogs/ericlippert/the-root-of-all-evil-part-one
    https://docs.microsoft 에서 전체 끔찍한 이야기를 읽을 수 있습니다. .com / en-us / archive / blogs / ericlippert / the-root-of-all-evil-part-two
    (강의 : 이전 버그를 수정하면서 새로운 더 나쁜 버그를 도입하는 것은 매우 쉽습니다.)

  • C # 팀은 강력한 이점이없는 기존 코드를 손상시킬 위험이 너무 높기 때문에이 버그가있는 동작을 수정하기보다는 숨기기로 결정했습니다. (강의 : 처음부터 올바르게!)

  • 이 동작을 유지하기 위해 Roslyn에서 작성한 코드 IsConstantNumericZerohttps://github.com/dotnet/roslyn/blob/master/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs 의 메서드 에서 찾을 수 있습니다. -Roslyn 행동이 정확히 무엇인지에 대한 자세한 내용은 그것을 참조하십시오. 저는 Conversions 디렉토리에 거의 모든 코드를 작성했습니다. 주석의 사양과 C #이 어떻게 다른지에 대한 흥미로운 사실이 많이 있으므로 모든 내용을 읽어 보시기 바랍니다. 쉽게 찾을 수 있도록 각각 SPEC VIOLATION으로 장식했습니다.

한 가지 더 주목할 점 : C #에서는 0 여부에 관계없이 열거 형 이니셜 라이저에서 모든 열거 형 값 을 사용할 수도 있습니다 .

enum E { A = 1 }
enum F { B = E.A }  // ???

사양은 이것이 합법적이어야하는지 아닌지에 대해 다소 모호하지만, 이것은 오랫동안 컴파일러에 있었기 때문에 새로운 컴파일러가 동작을 유지할 가능성이 높습니다.


답변

C #의 열거 형은 정의에 따른 정수 값입니다. 일관성을 위해 C #은 이러한 할당을 허용해서는 0.0안되지만 자동으로 integer로 처리됩니다 0. 이것은 아마도 C의 홀드 오버 일 것입니다. 여기서 리터럴 0은 특별히 처리되었으며 정수, 부동 소수점 숫자, 널 포인터 등 주어진 유형을 본질적으로 취할 수 있습니다.


답변

enum 은 실제로 (지원하는 모든 언어에서) 숫자 값이 아닌 의미 있고 고유 한 문자열 (레이블)로 작업하는 방법이되도록 의도되었습니다. 따라서 예제에서 Foo 열거 데이터 유형을 처리 할 때 BarBaz 만 사용해야 합니다. 많은 컴파일러가 정수를 사용할 수 있도록 허용하더라도 (열거 형은 일반적으로 내부적 으로 정수임) 정수 를 사용 (비교 또는 할당)해서는 안됩니다 .이 경우 0.0은 컴파일러에서 부주의하게 0으로 처리됩니다.

개념적으로, 열거 된 값에 정수 n 을 추가하거나 , n 값을 더 아래로 가져 오거나, val2val1 을 사용하여 얼마나 멀리 떨어져 있는지 확인하는 것이 좋습니다 . 그러나 언어 사양에서 명시 적으로 허용하지 않는 한, I 그것을 피할 것입니다. (열거 형 값을 사용할 수있는 방식에서 C 포인터와 같은 것으로 생각하십시오.) 열거 형을 부동 소수점 숫자와 그 사이에 고정 된 증분으로 구현할 수없는 이유는 없지만 들어 본 적이 없습니다. 이것은 모든 언어로 이루어집니다.


답변