How do I cast a generic enum to int?

C#Enums

C# Problem Overview


I have a small method that looks like this:

public void SetOptions<T>() where T : Enum
{
    int i = 0;
    foreach (T obj in Enum.GetValues(typeof(T)))
    {
        if (i == 0)
            DefaultOption = new ListItem(obj.Description(), obj.ToString());
        i++;
        DropDownList.Items.Add(new ListItem(obj.Description(), obj.ToString()));
    }
}

Basically, I populate a dropdown list from an enum. Description() is actually an extension method for enums, so T is definitely an enum.

However, I want to cast obj just as you would any enum to its index like this (int)obj, but I get an error saying I can't convert T to int. Is there a way to do this?

C# Solutions


Solution 1 - C#

You could also cast your value to object first and then to int.

###C# 7.3 and above

With the Enum generic constraint.

public static int EnumToInt<TValue>(this TValue value) where TValue : Enum
    => (int)(object)value;

###Below C# 7.3

Without the Enum generic constraint.

public static int EnumToInt<TValue>(this TValue value)  where TValue : struct, IConvertible
{
    if(!typeof(TValue).IsEnum)
    {
        throw new ArgumentException(nameof(value));
    }

    return (int)(object)value;
}

If your enum inherits from other types for example from byte the cast to int will throw an InvalidCastException.

You could either check if the base type of the enum is an integer.

public static int EnumToInt<TValue>(this TValue value) where TValue : Enum
{
    if (!typeof(int).IsAssignableFrom(Enum.GetUnderlyingType(typeof(TValue))))
        throw new ArgumentException(nameof(TValue));

    return (int)(object)value;
}

Or you if you use Convert.ToInt32 it will use the IConvertible interface of int32 to convert the incompatible types.

public static int EnumToInt<TValue>(this TValue value) where TValue : Enum
    => Convert.ToInt32(value);

Just be aware the converting uint to int and signed/unsigned pairs can cause unintended behavior. (Boxing to IConvertible and the converting is less performant than just unboxing.)


I recommend creating a method for each enum base type to ensure the correct result is returned.

Solution 2 - C#

try this,

public void SetOptions<T>()
{
	Type genericType = typeof(T);
	if (genericType.IsEnum)
	{
		foreach (T obj in Enum.GetValues(genericType))
		{
			Enum test = Enum.Parse(typeof(T), obj.ToString()) as Enum;
			int x = Convert.ToInt32(test); // x is the integer value of enum
                        ..........
                        ..........
		}
	}
}

Solution 3 - C#

If you are targeting .NET Core, you can utilize Unsafe.As<TFrom, TTo> in the System.Runtime.CompilerServices namespace, as explained on MSDN. The advantage here is there will be no boxing done, which is the only real performance cost in the other answers here.

private static int EnumToInt<TEnum>(TEnum enumValue) where TEnum : Enum
{
    return Unsafe.As<TEnum, int>(enumValue);
}

Note that this approach suffers from the safe issue as other existing answers do: there is not a guarantee that the given enum is a compatible int type, which is likely the same reason this functionality is not baked-in. If you are using this approach internally where you can be sure that any enum passed to it is a compatible type, then this is likely the most efficient approach.

Here is a link to an issue on dotnet's GitHub page where where this issue was raised, and some of the developers elaborated a bit on this approach if you would like to learn more.

Solution 4 - C#

Here's my solution for C# 7.3 and up. Not an exact match to OP's question, but probably useful for people finding this from Google. The main advantage over the other answers is it returns a ulong, which means any of the permissible enum types will fit in it. I also made a comparison of the machine code for this and a few of the other answers. Yes, I was bored and in the mood for a little premature optimization.

private static unsafe ulong EnumAsUInt64<T>(T item) where T : unmanaged, Enum
{
    ulong x;
    if (sizeof(T) == 1)
        x = *(byte*)(&item);
    else if (sizeof(T) == 2)
        x = *(ushort*)(&item);
    else if (sizeof(T) == 4)
        x = *(uint*)(&item);
    else if (sizeof(T) == 8)
        x = *(ulong*)(&item);
    else
        throw new ArgumentException("Argument is not a usual enum type; it is not 1, 2, 4, or 8 bytes in length.");
    return x;
}

Solution 5 - C#

This one is working with any underlying type

Convert.ChangeType(value, Enum.GetUnderlyingType(value.GetType()))

For example when you want add a value to SqlCommand, which converts enums to 0 and you have to explicitly cast it to matching type. But we can write following extension:

public static void AddEnum(this SqlParameterCollection parameters, string parameterName, Enum value)
{
	parameters.AddWithValue(parameterName, Convert.ChangeType(value, Enum.GetUnderlyingType(value.GetType())));
}

Which is doing everything for us.

Solution 6 - C#

Can you abuse GetHashCode for this?

public enum MyEnum
{
  Foo = 100,
  Bar = 200,
  Fizz = 0
}

static void Main(string[] args)
{
  var i1 = MyEnum.Foo.GetHashCode();  // i1 = 100
  var i2 = MyEnum.Bar.GetHashCode();  // i2 = 200
  var i3 = MyEnum.Fizz.GetHashCode(); // i3 = 0
}

Please note: "GetHashCode() is by design useful for only one thing: putting an object in a hash table. Hence the name." - E. Lippert

Solution 7 - C#

Here's a simpler way.

Since Enum implements IConvertible we can use ToInt32(..).

int? value = (enumCandidate as IConvertible)?.ToInt32(CultureInfo.InvariantCulture.Numberformat);

Or if you'd like a general purpose method for generic enums:

public static int GetEnumValue<T>(T inputEnum) where T: struct, IConvertible
{
    Type t = typeof(T);
    if (!t.IsEnum)
    {
        throw new ArgumentException("Input type must be an enum.");
    }

    return inputEnum.ToInt32(CultureInfo.InvariantCulture.NumberFormat);

}

Or yet more general:

public static int GetEnumValue(object enumInput)
{
    Type t = enumInput.GetType();
    if (!t.IsEnum)
    {
        throw new ArgumentException("Input type must be an enum.");
    }

    return ((IConvertible)inputEnum).ToInt32(CultureInfo.InvariantCulture.NumberFormat);

}

Solution 8 - C#

if you restict the generic T to be an Enum using

where T: Enum

you can then use the one-liner below

public static int GetIndexFromEnum<T>(T enumValue) where T : Enum {
    int index = Convert.ToInt32(enumValue);
    return index;
}

This seems like the simplest solution, as long as you can guarantee T is going to be an Enum.

Solution 9 - C#

Just cast the generic T to object first

T value;
int int_value = (int)(object)value;

That's it.

Solution 10 - C#

I'm surprised your code works at all. http://msdn.microsoft.com/en-us/library/system.enum.getvalues.aspx">`Enum.GetValues`</a> returns an array of integers - which are the values you're looking for. And, as others have mentioned, https://stackoverflow.com/questions/79126/create-generic-method-constraining-t-to-an-enum">you can't constrain your generics to an enum.

Instead, you should probably call your Description method as a regular static method and not an extension method.

Solution 11 - C#

Using LINQ this can be done elegantly:

public static void SetOptions<T>(this DropDownList dropDownList)
{
    if (!typeof(T).IsEnum)
    {
        throw new ArgumentException("Type must be an enum type.");
    }

    dropDownList.Items.AddRange(Enum
        .GetValues(typeof(T))
        .Cast<Enum>()
        .Select(x => new ListItem(x.ToString(), Convert.ToInt32(x).ToString()))
        .ToArray());
}

Solution 12 - C#

Try this: (assuming that the TEnum has a numeration from 0 to n)

public void SetOptions<TEnum>() where TEnum : Enum
{
    foreach (TEnum obj in Enum.GetValues(typeof(TEnum)))
    {
        var i = (int)(object)obj;
        if (i == 0) DefaultOption = new ListItem(obj.Description(), obj.ToString());
        DropDownList.Items.Add(new ListItem(obj.Description(), obj.ToString()));
    }
}

Solution 13 - C#

To expand on Jan's answer concerning generics and enum values:

void MyFunc<T>(T value)
{
    Type t = typeof(T);
    if(t.IsEnum)
    {
        int valueAsInt = value.GetHashCode(); // returns the integer value
    }
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestiondotnetnoobView Question on Stackoverflow
Solution 1 - C#NtFreXView Answer on Stackoverflow
Solution 2 - C#petchirajanView Answer on Stackoverflow
Solution 3 - C#ForeverZer0View Answer on Stackoverflow
Solution 4 - C#scottView Answer on Stackoverflow
Solution 5 - C#Alex ZhukovskiyView Answer on Stackoverflow
Solution 6 - C#janView Answer on Stackoverflow
Solution 7 - C#Steve LautenschlagerView Answer on Stackoverflow
Solution 8 - C#Adam BView Answer on Stackoverflow
Solution 9 - C#manCuongCsView Answer on Stackoverflow
Solution 10 - C#zmbqView Answer on Stackoverflow
Solution 11 - C#matkView Answer on Stackoverflow
Solution 12 - C#EmilianoView Answer on Stackoverflow
Solution 13 - C#P. AveryView Answer on Stackoverflow