views:

168

answers:

4

I modified the extension method Thorarin gave in response to this question to work on an int instead of a string:

public static TEnum ToEnum<TEnum>(this int intEnumValue, TEnum defaultValue)
{
    if (!Enum.IsDefined(typeof(TEnum), intEnumValue))
        return defaultValue;
    return (TEnum)intEnumValue;
}

The compiler gives the error "Cannot convert type 'int' to 'TEnum'." on the last line.

If the line is changed to this:

return (TEnum)(object)intEnumValue;

it compiles and works.

Why does the int need to be casted to object first?

+4  A: 

It's impossible to directly cast an int to an unknown type, so the compiler won't allow (TEnum)intEnumValue. The two casts you are doing now are actually subtly different: (object) boxes the int, while (TEnum) casts the boxed int to a TEnum, which is statically allowed because an expression which is of static type object might actually be of runtime type TEnum.

There might also be some further subtlety: Normally, a boxed int can only be unboxed to int. I think I've explained why the conversion is allowed by the compiler, but not why it's also allowed by the runtime. Perhaps conversion to TEnum is only allowed at runtime because TEnum happens to be an enum that has int as its base type? I think I remember reading that enums in the CLR are really just instances of their base type.

Edit: I confirmed my suspicion:

public static void Main()
{
    int value = 1;

    IntEnum i = ToEnum<IntEnum>(value); // Valid cast, runs fine.
    ByteEnum b = ToEnum<ByteEnum>(value); // Invalid cast exception!
}

public enum ByteEnum : byte { }

public enum IntEnum : int { }

public static TEnum ToEnum<TEnum>(int value)
{
    return (TEnum)(object)value;
}

So we can safely say that (TEnum) on a boxed int is only valid if TEnum is in fact an int under the covers!

Joren
Is there a more direct method to go from an Enum's underlying type to the Enum?
Kathy Van Stone
Enum.ToObject(typeof(TEnum), intEnumValue) as TEnum
plinth
A: 

When you cast with (TEnum)intEnumValue, the compiler doesn't know what kind of class TEnum is going to be. Since int can only be implicitly converted to certain classes, the compiler fails. Casting to object is acceptable, and then TEnum is an object, so that cast is also possible.

JustLoren
A: 

TEnum is an arbitrary type. Can you cast an int to Rectangle? To DateTime? The compiler doesn't know and unfortunately the spec says you can't add a constraint on it either.

plinth
+1  A: 

Eric Lippert has a blog post about this sort of issue here. Basically, there are several different types of 'cast', and going from int to TEnum could be one of several. So, the only safe way to do it is to box the int to an object, then try and convert it to a TEnum at runtime. You cannot go straight from int to TEnum, as what needs to be done depends on what TEnum is, and that is only known at runtime.

thecoop