The_Black_Smurf
The_Black_Smurf

Reputation: 5269

Cast Int to Generic Nullable Enum in C#

I would like to cast an int value as nullable enum used a generic function. I thought it would be easy, especially with all the with all the SO about the enum / int casting. The closest question I've found is this one, but unfortunately, it doesn't handle the nullable enums. This question solve the Nullable enums casting question but not with the generic.

Here's a simplified example of what I'm trying to do:

public enum SouthParkCharacters
{
    Stan = 0,
    Kyle = 1,
    Eric = 2,
    Kenny = 3
}

public static T CastNullableEnum<T>(int value)
{
    return (T)(object)((int?)value).Value;
}


public static T SomeFunction<T>(this int anyEnumValue)
{
        SouthParkCharacters? spc = SouthParkCharacters.Kenny;

        spc = CastNullableEnum<SouthParkCharacters>(3); //I am working!
        spc = CastNullableEnum<SouthParkCharacters?>(3); //Raise error Specified cast is not valid.

        //This function will be public so I won't have control over the T
        T finalValue = CastNullableEnum<T>(anyEnumValue); //Raise error Specified cast is not valid.

        return finalValue;

}

As you can see in last function, a cast for a non-nullable enum is working. The problem is that I want to be able to call the CastNullableEnum from an other generic function, which could use a nullable enum as a generic type.

I know you can pass a generic type as a nullable generic CastNullableEnum<T?> but can we do the opposite (i.e. pass the nullable underlying type to generic function)?

If this can't be done, then I guess the solution would be to find the right way to cast an enum value in the CastNullableEnum function.

Upvotes: 1

Views: 5068

Answers (3)

Bigjim
Bigjim

Reputation: 2255

This will work:

public static T IntToEnum<T>(object value)
{
    if (value == null)
        return (T)value;

    else if (typeof(T).IsEnum)
        return (T)value;

    else if (Nullable.GetUnderlyingType(typeof(T))?.IsEnum == true)
        return (T)Enum.ToObject(Nullable.GetUnderlyingType(typeof(T)), (int)value);

    throw new Exception(string.Format("Value cannot be converted from {0} to {1}", value.GetType().Name, typeof(T).Name));
}

can be used for nullable and regular enums

public enum Codes
{
    A = 1,
    B = 2,
    C = 3
}

static void Main(string[] args)
{
    Console.WriteLine(IntToEnum<Codes?>(1));
    Console.WriteLine(IntToEnum<Codes?>(null));
    Console.WriteLine(IntToEnum<Codes>(2));
}

Upvotes: 3

sa_ddam213
sa_ddam213

Reputation: 43616

I probably read the question wrong, buy you could grab the underlying type from the nullable and use Enum.Parse instead of a direct cast.

public T CastNullableEnum<T>(int value) 
{
    var enumType = Nullable.GetUnderlyingType(typeof(T));
    if (Enum.IsDefined(enumType, value)) 
    {
        return (T)(object)Enum.Parse(enumType, value.ToString());
    }
    return default(T);
}

var test1 = CastNullableEnum<SouthParkCharacters?>(3); 
//returns Kenny

var test2 = CastNullableEnum<SouthParkCharacters?>(9); 
// returns null

Upvotes: 7

Hiệp L&#234;
Hiệp L&#234;

Reputation: 634

Interesting question. The problem with this code is to cast integer to nullable enum, which is not possible.

  SouthParkCharacters? enumValue = (SouthParkCharacters?)int.MaxValue;
  if (enumValue.HasValue)
     {
         Console.WriteLine("it still has value");
     }

The above code is running and it results the enumValue still HasValue.

But anyway, if you take a look at the C# code after the compiling, it will be something like this:

public static T CastNullableEnum<T>(int value)
    {
        Type type = typeof(T);
        return (T)((object)value);
    }
    public static T SomeFunction<T>(int anyEnumValue)
    {
        Program.SouthParkCharacters? spc = new Program.SouthParkCharacters?(Program.SouthParkCharacters.Kenny);
        spc = new Program.SouthParkCharacters?(Program.CastNullableEnum<Program.SouthParkCharacters>(new int?(3)));
        spc = Program.CastNullableEnum<Program.SouthParkCharacters?>(new int?(3));
        return Program.CastNullableEnum<T>(new int?(anyEnumValue));
    }

Now, notice the

spc = new Program.SouthParkCharacters?(Program.CastNullableEnum<Program.SouthParkCharacters>(new int?(3))); 

That's where the Unspecified Cast Exception comes from; because the new SouthParkCharacters?(int value) expect an Integer.

Funny thing is that you won't encounter this problem when you run with Immediate. Interesting, huh?

Upvotes: 1

Related Questions