카테고리 보관물: C#

C#

int를 잘못된 enum 값으로 캐스팅하면 예외가 발생하지 않는 이유는 무엇입니까? 20, Unknown } int이

다음과 같은 열거 형이있는 경우 :

enum Beer
{
    Bud = 10,
    Stella = 20,
    Unknown
}

int이 값을 벗어난을 유형으로 캐스팅 할 때 왜 예외가 발생하지 Beer않습니까?

예를 들어 다음 코드는 예외를 throw하지 않고 콘솔에 ’50’을 출력합니다.

int i = 50;
var b = (Beer) i;

Console.WriteLine(b.ToString());

나는 이것이 이상하다는 것을 알았다. 누구든지 명확히 할 수 있는가?



답변

에서 촬영 열거 구문 분석과 혼란

이것은 .NET을 만든 사람들의 결정이었습니다. 열거가 (다른 값 유형에 의해 백업됩니다 int, short,byte 실제로 그 값 유형에 대한 유효 값을 가질 수 있도록, 등)합니다.

나는 개인적으로 이것이 작동하는 방식을 좋아하지 않으므로 일련의 유틸리티 방법을 만들었습니다.

/// <summary>
/// Utility methods for enum values. This static type will fail to initialize 
/// (throwing a <see cref="TypeInitializationException"/>) if
/// you try to provide a value that is not an enum.
/// </summary>
/// <typeparam name="T">An enum type. </typeparam>
public static class EnumUtil<T>
    where T : struct, IConvertible // Try to get as much of a static check as we can.
{
    // The .NET framework doesn't provide a compile-checked
    // way to ensure that a type is an enum, so we have to check when the type
    // is statically invoked.
    static EnumUtil()
    {
        // Throw Exception on static initialization if the given type isn't an enum.
        Require.That(typeof (T).IsEnum, () => typeof(T).FullName + " is not an enum type.");
    }

    /// <summary>
    /// In the .NET Framework, objects can be cast to enum values which are not
    /// defined for their type. This method provides a simple fail-fast check
    /// that the enum value is defined, and creates a cast at the same time.
    /// Cast the given value as the given enum type.
    /// Throw an exception if the value is not defined for the given enum type.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="enumValue"></param>
    /// <exception cref="InvalidCastException">
    /// If the given value is not a defined value of the enum type.
    /// </exception>
    /// <returns></returns>
    public static T DefinedCast(object enumValue)

    {
        if (!System.Enum.IsDefined(typeof(T), enumValue))
            throw new InvalidCastException(enumValue + " is not a defined value for enum type " +
                                           typeof (T).FullName);
        return (T) enumValue;
    }

    /// <summary>
    /// 
    /// </summary>
    /// <param name="enumValue"></param>
    /// <returns></returns>
    public static T Parse(string enumValue)
    {
        var parsedValue = (T)System.Enum.Parse(typeof (T), enumValue);
        //Require that the parsed value is defined
        Require.That(parsedValue.IsDefined(),
            () => new ArgumentException(string.Format("{0} is not a defined value for enum type {1}",
                enumValue, typeof(T).FullName)));
        return parsedValue;
    }

    public static bool IsDefined(T enumValue)
    {
        return System.Enum.IsDefined(typeof (T), enumValue);
    }

}


public static class EnumExtensions
{
    public static bool IsDefined<T>(this T enumValue)
        where T : struct, IConvertible
    {
        return EnumUtil<T>.IsDefined(enumValue);
    }
}

이렇게 말할 수 있습니다.

if(!sEnum.IsDefined()) throw new Exception(...);

… 또는 :

EnumUtil<Stooge>.Parse(s); // throws an exception if s is not a defined value.

편집하다

위에 제공된 설명 외에도 Enum의 .NET 버전은 Java에서 영감을받은 패턴보다 C에서 영감을받은 패턴을 따릅니다. 이렇게하면 이진 패턴을 사용하여 특정 “플래그”가 열거 형 값에서 활성 상태인지 여부를 확인할 수 있는 “비트 플래그”열거 형 을 가질 수 있습니다. 가능한 모든 플래그 조합 (예 : MondayAndTuesday, MondayAndWednesdayAndThursday) 을 정의해야한다면 매우 지루할 것입니다. 따라서 정의되지 않은 열거 형 값을 사용할 수있는 능력이 있으면 정말 편리 할 수 ​​있습니다. 이러한 종류의 트릭을 활용하지 않는 enum 유형에 대한 빠른 실패 동작을 원할 때 약간의 추가 작업이 필요합니다.


답변

열거 형은 종종 플래그로 사용됩니다.

[Flags]
enum Permission
{
    None = 0x00,
    Read = 0x01,
    Write = 0x02,
}
...

Permission p = Permission.Read | Permission.Write;

p의 값은 정수 3이며 열거 형의 값은 아니지만 유효한 값입니다.

나는 개인적으로 다른 해결책을 보았을 것입니다. 나는 “비트 배열”정수 유형 “고유 한 값의 세트”유형을 둘 다 “열거 형”으로 병합하는 것보다 두 가지 다른 언어 기능으로 만드는 기능을 갖고 싶었습니다. 하지만 이것이 원래 언어와 프레임 워크 디자이너가 생각 해낸 것입니다. 결과적으로 우리는 선언되지 않은 열거 형 값을 합법적 인 값으로 허용해야합니다.


답변

짧은 대답 : 언어 디자이너는 이러한 방식으로 언어를 디자인하기로 결정했습니다.

Section 6.2.2: Explicit enumeration conversionsC # 언어 사양 의 긴 대답은 다음 과 같습니다.

두 유형 간의 명시 적 열거 변환은 참여하는 열거 형 유형을 해당 열거 형 유형의 기본 유형으로 처리 한 다음 결과 유형간에 암시 적 또는 명시 적 숫자 변환을 수행하여 처리됩니다. 예를 들어, 기본 유형이 int 인 enum 유형 E가있는 경우 E에서 바이트로의 변환은 int에서 바이트로의 명시 적 숫자 변환 (§6.2.1)으로 처리되고 바이트에서 E 로의 변환은 다음과 같이 처리됩니다. byte에서 int 로의 암시 적 숫자 변환 (§6.1.2)

기본적으로 enum 은 변환 작업을 수행 할 때 기본 유형으로 처리됩니다. 기본적으로 enum 의 기본 유형은 Int32입니다. 즉, 변환이 로의 변환과 똑같이 처리됩니다 Int32. 이는 모든 유효한 int값이 허용됨을 의미합니다 .

나는 이것이 주로 성능상의 이유로 수행되었다고 생각합니다. enum간단한 정수 형식 을 만들고 정수 형식 변환을 허용하면 CLR이 모든 추가 검사를 수행 할 필요가 없습니다. 이는 enum정수를 사용하는 것과 비교할 때 an을 사용하는 것이 실제로 성능 손실 이 없음을 의미하므로 사용 을 장려하는 데 도움이됩니다.