1. ホーム
  2. c#

[解決済み] intを無効なenum値にキャストすると例外が発生しないのはなぜですか?

2022-05-29 20:21:58

質問

このようなenumがあった場合

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

をキャストする際に例外が発生しないのはなぜですか? int の型にキャストする際に例外が発生しないのはなぜでしょうか? Beer ?

例えば次のコードは例外をスローせず、コンソールに「50」を出力します。

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

Console.WriteLine(b.ToString());

私はこれが奇妙に思える...誰か明確にすることができますか?

どのように解決するのですか?

引用元 Enumのパースで混乱する

これは、.NETを作った人たちの判断です。enum は、別の値型 ( 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.

編集

上記の説明に加えて、.NET版のEnumはJavaにインスパイアされたものよりもCにインスパイアされたパターンに従っていることを認識する必要があります。これによって ビット フラグの列挙が可能です。 で、バイナリ パターンを使用して、特定の "flag" が enum 値でアクティブであるかどうかを判断することができます。もし、可能な限りのフラグの組み合わせを定義しなければならないとしたら (すなわち MondayAndTuesday , MondayAndWednesdayAndThursday など)、これらは非常に面倒なことです。ですから、未定義の列挙型の値を使用する能力があると、本当に便利です。この種のトリックを活用しない列挙型のフェールファスト動作が必要な場合、少し余分な作業が必要になります。