Int to Enum Extension Method As you know, converting an int to an enum is straightforward. Doing it generically with type/range checking isn't. I looked for a reusable example since this seems like a common situation but did not find a suitable one. If it exists, please let me know. Below is a C# extension method that encapsulates this behavior. Any feedback is welcome. 

public static T ToEnum&lt;T&gt;(this int value)
{
    Type type = typeof(T);

    if (!type.IsEnum)
    {
        throw new ArgumentException($"{type} is not an enum.");
    }

    if (!type.IsEnumDefined(value))
    {
        throw new ArgumentException($"{value} is not a valid ordinal of type {type}.");
    }

    return (T)Enum.ToObject(type, value);
}
As Jason Tyler said in a comment, this has a problem that [Flags] enumerations won't be properly handled.

I ran this in C# interactive, and it threw an exception (as expected, but not desired) for a valid [Flags] value:


&gt; public static T ToEnum&lt;T&gt;(this int value)
. {
.     Type type = typeof(T);
. 
.     if (!type.IsEnum)
.     {
.         throw new ArgumentException($"{type} is not an enum.");
. 
.     }
. 
.     if (!type.IsEnumDefined(value))
.     {
.         throw new ArgumentException($"{value} is not a valid ordinal of type {type}.");
.     }
. 
.     return (T)Enum.ToObject(type, value);
. }
&gt; [Flags] public enum Test { Flag1 = 0x01, Flag2 = 0x02 }
&gt; ToEnum&lt;Test&gt;(3);
3 is not a valid ordinal of type Submission#1+Test.
  + Submission#0.ToEnum&lt;T&gt;(int)



So, we might have to look at a different way of going about this.

There's an answer on Stack Overflow, but I don't like it. They use string comparison and validation, and it just feels like a hack.

So what can we do?

Well, we have Enum.GetValues, I bet we can come up with a solution there, assuming we interpret all the values as a numeric type.

The hard part is picking the type. Enums can be any numeric type, so we have to decide on one.

Fun fact: every numeric type can be cast to the others in C#, there just might be overflow issues. So if we look at all the numeric types, we can find that we should always be able to cast them to a long or ulong, depending on whether they're signed or not.

OK, so how do we figure out what number type it is?

There's another magic function in Enum: Enum.GetUnderlyingType, I think that'll do.

Of course, this also means we can no longer take an int input, because we want to support any number. We could take in an object, but then we have to deal with boxing/unboxing, and that can get prohibitively expensive with frequent use, but we'll work that out-of-the-way later.

We can use some bit-fiddling to find out if a value is only composed of valid Enum values. Basically, we'll do the following:


Loop through the valid flags options:


If providedValue ∧ enumValue &gt; 0, then providedValue = providedValue ⊕ enumValue

If providedValue &gt; 0 then it is not composed strictly of the valid enum options


For those unfamiliar with boolean algebra:


∧ = "AND", take all bits of both values and compare them such that the result value only contains those that are 1 in each value, this is &amp; in C#;
⊕ = "XOR", take all bits of both values and compare them such that the result value only contains those that are 1 in one value, and 0 in another, this is ^ in C#;


So, if we rewrite this, it gets a bit ugly, but we'll clean some of that up:

public static TEnum ToEnum&lt;TEnum&gt;(this object value)
{
    Type type = typeof(TEnum);

    if (!type.IsEnum)
    {
        throw new ArgumentException($"{type} is not an enum.");
    }

    if (type.GetCustomAttributes(typeof(FlagsAttribute), true).Length &gt; 0)
    {
        var values = Enum.GetValues(type);
        switch (Enum.GetUnderlyingType(type).FullName)
        {
            case "System.Byte":
                {
                    var myVal = (byte)value;
                    foreach (byte val in values)
                    {
                        if ((myVal &amp; val) &gt; (byte)0)
                        {
                            myVal ^= val;
                        }
                    }
                    if (myVal &gt; (byte)0)
                    {
                        throw new ArgumentException($"{value} is not a valid ordinal of type {type}.");
                    }
                }
                break;
            case "System.SByte":
                {
                    var myVal = (sbyte)value;
                    foreach (sbyte val in values)
                    {
                        if ((myVal &amp; val) &gt; (sbyte)0)
                        {
                            myVal ^= val;
                        }
                    }
                    if (myVal &gt; (sbyte)0)
                    {
                        throw new ArgumentException($"{value} is not a valid ordinal of type {type}.");
                    }
                }
                break;
            case "System.UInt16":
                {
                    var myVal = (ushort)value;
                    foreach (ushort val in values)
                    {
                        if ((myVal &amp; val) &gt; (ushort)0)
                        {
                            myVal ^= val;
                        }
                    }
                    if (myVal &gt; (ushort)0)
                    {
                        throw new ArgumentException($"{value} is not a valid ordinal of type {type}.");
                    }
                }
                break;
            case "System.Int16":
                {
                    var myVal = (short)value;
                    foreach (short val in values)
                    {
                        if ((myVal &amp; val) &gt; (short)0)
                        {
                            myVal ^= val;
                        }
                    }
                    if (myVal &gt; (short)0)
                    {
                        throw new ArgumentException($"{value} is not a valid ordinal of type {type}.");
                    }
                }
                break;
            case "System.UInt32":
                {
                    var myVal = (uint)value;
                    foreach (uint val in values)
                    {
                        if ((myVal &amp; val) &gt; (uint)0)
                        {
                            myVal ^= val;
                        }
                    }
                    if (myVal &gt; (uint)0)
                    {
                        throw new ArgumentException($"{value} is not a valid ordinal of type {type}.");
                    }
                }
                break;
            case "System.Int32":
                {
                    var myVal = (int)value;
                    foreach (int val in values)
                    {
                        if ((myVal &amp; val) &gt; (int)0)
                        {
                            myVal ^= val;
                        }
                    }
                    if (myVal &gt; (int)0)
                    {
                        throw new ArgumentException($"{value} is not a valid ordinal of type {type}.");
                    }
                }
                break;
            case "System.UInt64":
                {
                    var myVal = (ulong)value;
                    foreach (ulong val in values)
                    {
                        if ((myVal &amp; val) &gt; (ulong)0)
                        {
                            myVal ^= val;
                        }
                    }
                    if (myVal &gt; (ulong)0)
                    {
                        throw new ArgumentException($"{value} is not a valid ordinal of type {type}.");
                    }
                }
                break;
            case "System.Int64":
                {
                    var myVal = (long)value;
                    foreach (long val in values)
                    {
                        if ((myVal &amp; val) &gt; (long)0)
                        {
                            myVal ^= val;
                        }
                    }
                    if (myVal &gt; (long)0)
                    {
                        throw new ArgumentException($"{value} is not a valid ordinal of type {type}.");
                    }
                }
                break;
            default:
                throw new ArgumentException($"{type} does not have a valid backing type ({underlyingType}).");
                break;
        }
    }
    else
    {
        if (!type.IsEnumDefined(value))
        {
            throw new ArgumentException($"{value} is not a valid ordinal of type {type}.");
        }
    }

    return (TEnum)Enum.ToObject(type, value);
}


This will work as expected on [Flags] enumerations, which is great! But, there's some improvement to be made.

First and foremost: how do we get rid of that object parameter?

Well, we have a few options, but the easiest is to redefine our ToEnum on each numeric type, and create a version that takes a dynamic. It's ugly, but works:

public static TEnum ToEnum&lt;TEnum&gt;(this byte value) =&gt; DynamicToEnum&lt;TEnum&gt;(value);
public static TEnum ToEnum&lt;TEnum&gt;(this sbyte value) =&gt; DynamicToEnum&lt;TEnum&gt;(value);
public static TEnum ToEnum&lt;TEnum&gt;(this ushort value) =&gt; DynamicToEnum&lt;TEnum&gt;(value);
public static TEnum ToEnum&lt;TEnum&gt;(this short value) =&gt; DynamicToEnum&lt;TEnum&gt;(value);
public static TEnum ToEnum&lt;TEnum&gt;(this uint value) =&gt; DynamicToEnum&lt;TEnum&gt;(value);
public static TEnum ToEnum&lt;TEnum&gt;(this int value) =&gt; DynamicToEnum&lt;TEnum&gt;(value);
public static TEnum ToEnum&lt;TEnum&gt;(this ulong value) =&gt; DynamicToEnum&lt;TEnum&gt;(value);
public static TEnum ToEnum&lt;TEnum&gt;(this long value) =&gt; DynamicToEnum&lt;TEnum&gt;(value);

private static TEnum DynamicToEnum&lt;TEnum&gt;(dynamic value)
{
    ...


For each repeated case body, there's no good way to do this. With the use dynamic, we lose all safety of our types. There's no way to constrain it generically, because we can't say "it can be any number."

The best we could do would be a local function something like the following:

void testEnum&lt;T&gt;(T myVal, Func&lt;T, bool&gt; aboveZero, Func&lt;T, T, T&gt; and, Func&lt;T, T, T&gt; xor)
{
    foreach (T val in values)
    {
        if (aboveZero(and(myVal, val)))
        {
            myVal = xor(myVal, val);
        }
    }
    if (aboveZero(myVal))
    {
        throw new ArgumentException($"{value} is not a valid ordinal of type {type}.");
    }
}


Which means, we can use it to create a more readable version of our function:

public static TEnum ToEnum&lt;TEnum&gt;(this byte value) =&gt; DynamicToEnum&lt;TEnum&gt;(value);
public static TEnum ToEnum&lt;TEnum&gt;(this sbyte value) =&gt; DynamicToEnum&lt;TEnum&gt;(value);
public static TEnum ToEnum&lt;TEnum&gt;(this ushort value) =&gt; DynamicToEnum&lt;TEnum&gt;(value);
public static TEnum ToEnum&lt;TEnum&gt;(this short value) =&gt; DynamicToEnum&lt;TEnum&gt;(value);
public static TEnum ToEnum&lt;TEnum&gt;(this uint value) =&gt; DynamicToEnum&lt;TEnum&gt;(value);
public static TEnum ToEnum&lt;TEnum&gt;(this int value) =&gt; DynamicToEnum&lt;TEnum&gt;(value);
public static TEnum ToEnum&lt;TEnum&gt;(this ulong value) =&gt; DynamicToEnum&lt;TEnum&gt;(value);
public static TEnum ToEnum&lt;TEnum&gt;(this long value) =&gt; DynamicToEnum&lt;TEnum&gt;(value);

private static TEnum DynamicToEnum&lt;TEnum&gt;(dynamic value)
{
    Type type = typeof(TEnum);

    if (!type.IsEnum)
    {
        throw new ArgumentException($"{type} is not an enum.");
    }

    if (type.GetCustomAttributes(typeof(FlagsAttribute), true).Length &gt; 0)
    {
        var values = Enum.GetValues(type);

        void testEnum&lt;T&gt;(T myVal, Func&lt;T, bool&gt; aboveZero, Func&lt;T, T, T&gt; and, Func&lt;T, T, T&gt; xor)
        {
            foreach (T val in values)
            {
                if (aboveZero(and(myVal, val)))
                {
                    myVal = xor(myVal, val);
                }
            }
            if (aboveZero(myVal))
            {
                throw new ArgumentException($"{value} is not a valid ordinal of type {type}.");
            }
        }

        var underlyingType = Enum.GetUnderlyingType(type).FullName;
        switch (underlyingType)
        {
            case "System.Byte":
                testEnum((Byte)value, (v) =&gt; v &gt; (Byte)0, (v1, v2) =&gt; (Byte)(v1 &amp; v2), (v1, v2) =&gt; (Byte)(v1 ^ v2));
                break;
            case "System.SByte":
                testEnum((SByte)value, (v) =&gt; v &gt; (SByte)0, (v1, v2) =&gt; (SByte)(v1 &amp; v2), (v1, v2) =&gt; (SByte)(v1 ^ v2));
                break;
            case "System.UInt16":
                testEnum((UInt16)value, (v) =&gt; v &gt; (UInt16)0, (v1, v2) =&gt; (UInt16)(v1 &amp; v2), (v1, v2) =&gt; (UInt16)(v1 ^ v2));
                break;
            case "System.Int16":
                testEnum((Int16)value, (v) =&gt; v &gt; (Int16)0, (v1, v2) =&gt; (Int16)(v1 &amp; v2), (v1, v2) =&gt; (Int16)(v1 ^ v2));
                break;
            case "System.UInt32":
                testEnum((UInt32)value, (v) =&gt; v &gt; (UInt32)0, (v1, v2) =&gt; (UInt32)(v1 &amp; v2), (v1, v2) =&gt; (UInt32)(v1 ^ v2));
                break;
            case "System.Int32":
                testEnum((Int32)value, (v) =&gt; v &gt; (Int32)0, (v1, v2) =&gt; (Int32)(v1 &amp; v2), (v1, v2) =&gt; (Int32)(v1 ^ v2));
                break;
            case "System.UInt64":
                testEnum((UInt64)value, (v) =&gt; v &gt; (UInt64)0, (v1, v2) =&gt; (UInt64)(v1 &amp; v2), (v1, v2) =&gt; (UInt64)(v1 ^ v2));
                break;
            case "System.Int64":
                testEnum((Int64)value, (v) =&gt; v &gt; (Int64)0, (v1, v2) =&gt; (Int64)(v1 &amp; v2), (v1, v2) =&gt; (Int64)(v1 ^ v2));
                break;
            default:
                throw new ArgumentException($"{type} does not have a valid backing type ({underlyingType}).");
                break;
        }
    }
    else
    {
        if (!type.IsEnumDefined(value))
        {
            throw new ArgumentException($"{value} is not a valid ordinal of type {type}.");
        }
    }

    return (TEnum)Enum.ToObject(type, value);
}


Overboard? Probably. But does it work? Oh yeah.

Additionally, as Peter Taylor mentions, you can use where TEnum : struct to help reduce some of the run-time issues, and bring them to the compile-type.