Why does casting int to invalid enum value NOT throw exception?

Enums are often used as flags:

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

Permission p = Permission.Read | Permission.Write;

The value of p is the integer 3, which is not a value of the enum, but clearly is a valid value.

I personally would rather have seen a different solution; I would rather have had the ability to make "bit array" integer types and "a set of distinct value" types as two different language features rather than conflating them both into "enum". But that's what the original language and framework designers came up with; as a result, we have to allow non-declared values of the enum to be legal values.


Taken from Confusion with parsing an Enum

This was a decision on the part of the people who created .NET. An enum is backed by another value type (int, short, byte, etc), and so it can actually have any value that is valid for those value types.

I personally am not a fan of the way this works, so I made a series of utility methods:

/// <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);
    }
}

This way, I can say:

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

... or:

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

Edit

Beyond the explanation given above, you have to realize that the .NET version of Enum follows a more C-inspired pattern than a Java-inspired one. This makes it possible to have "Bit Flag" enums which can use binary patterns to determine whether a particular "flag" is active in an enum value. If you had to define every possible combination of flags (i.e. MondayAndTuesday, MondayAndWednesdayAndThursday), these would be extremely tedious. So having the capacity to use undefined enum values can be really handy. It just requires a little extra work when you want a fail-fast behavior on enum types that don't leverage these sorts of tricks.


From the documentation:

A variable of type Days can be assigned any value in the range of the underlying type; the values are not limited to the named constants.


The short answer: The language designers decided to design the language this way.

The long answer: Section 6.2.2: Explicit enumeration conversions of the C# Language Specification says:

An explicit enumeration conversion between two types is processed by treating any participating enum-type as the underlying type of that enum-type, and then performing an implicit or explicit numeric conversion between the resulting types. For example, given an enum-type E with and underlying type of int, a conversion from E to byte is processed as an explicit numeric conversion (§6.2.1) from int to byte, and a conversion from byte to E is processed as an implicit numeric conversion (§6.1.2) from byte to int.

Basically, the enum is treated as the underlying type when it comes to do a conversion operation. By default, an enum's underlying type is Int32, which means the conversion is treated exactly like a conversion to Int32. This means any valid int value is allowable.

I suspect this was done mainly for performance reasons. By making enum a simple integral type and allowing any integral type conversion, the CLR doesn't need to do all of the extra checks. This means that using an enum doesn't really have any performance loss when compared to using an integer, which in turn helps encourage its use.

Tags:

C#