Validate Enum Values

Viewed 75847

I need to validate an integer to know if is a valid enum value.

What is the best way to do this in C#?

12 Answers

You got to love these folk who assume that data not only always comes from a UI, but a UI within your control!

IsDefined is fine for most scenarios, you could start with:

public static bool TryParseEnum<TEnum>(this int enumValue, out TEnum retVal)
{
 retVal = default(TEnum);
 bool success = Enum.IsDefined(typeof(TEnum), enumValue);
 if (success)
 {
  retVal = (TEnum)Enum.ToObject(typeof(TEnum), enumValue);
 }
 return success;
}

(Obviously just drop the ‘this’ if you don’t think it’s a suitable int extension)

Here is a fast generic solution, using a statically-constucted HashSet<T>.

You can define this once in your toolbox, and then use it for all your enum validation.

public static class EnumHelpers
{
    /// <summary>
    /// Returns whether the given enum value is a defined value for its type.
    /// Throws if the type parameter is not an enum type.
    /// </summary>
    public static bool IsDefined<T>(T enumValue)
    {
        if (typeof(T).BaseType != typeof(System.Enum)) throw new ArgumentException($"{nameof(T)} must be an enum type.");

        return EnumValueCache<T>.DefinedValues.Contains(enumValue);
    }

    /// <summary>
    /// Statically caches each defined value for each enum type for which this class is accessed.
    /// Uses the fact that static things exist separately for each distinct type parameter.
    /// </summary>
    internal static class EnumValueCache<T>
    {
        public static HashSet<T> DefinedValues { get; }

        static EnumValueCache()
        {
            if (typeof(T).BaseType != typeof(System.Enum)) throw new Exception($"{nameof(T)} must be an enum type.");

            DefinedValues = new HashSet<T>((T[])System.Enum.GetValues(typeof(T)));
        }
    }
}

Note that this approach is easily extended to enum parsing as well, by using a dictionary with string keys (minding case-insensitivity and numeric string representations).

Brad Abrams specifically warns against Enum.IsDefined in his post The Danger of Oversimplification.

The best way to get rid of this requirement (that is, the need to validate enums) is to remove ways where users can get it wrong, e.g., an input box of some sort. Use enums with drop downs, for example, to enforce only valid enums.

Building upon Timo's answer, here is an even faster, safer and simpler solution, provided as an extension method.

public static class EnumExtensions
{
    /// <summary>Whether the given value is defined on its enum type.</summary>
    public static bool IsDefined<T>(this T enumValue) where T : Enum
    {
        return EnumValueCache<T>.DefinedValues.Contains(enumValue);
    }
    
    private static class EnumValueCache<T> where T : Enum
    {
        public static readonly HashSet<T> DefinedValues = new HashSet<T>((T[])Enum.GetValues(typeof(T)));
    }
}

Usage:

if (myEnumValue.IsDefined()) { ... }

Update - it's even now cleaner in .NET 5:

public static class EnumExtensions
{
    /// <summary>Whether the given value is defined on its enum type.</summary>
    public static bool IsDefined<T>(this T enumValue) where T : struct, Enum
    {
        return EnumValueCache<T>.DefinedValues.Contains(enumValue);
    }

    private static class EnumValueCache<T> where T : struct, Enum
    {
        public static readonly HashSet<T> DefinedValues = new(Enum.GetValues<T>());
    }
}

You can use the FluentValidation for your project. Here is a simple example for the "Enum Validation"

Let's create a EnumValidator class with using FluentValidation;

public class EnumValidator<TEnum> : AbstractValidator<TEnum> where TEnum : struct, IConvertible, IComparable, IFormattable
{
    public EnumValidator(string message)
    {
        RuleFor(a => a).Must(a => typeof(TEnum).IsEnum).IsInEnum().WithMessage(message);
    }

}

Now we created the our enumvalidator class; let's create the a class to call enumvalidor class;

 public class Customer 
{
  public string Name { get; set; }
  public Address address{ get; set; }
  public AddressType type {get; set;}
}
public class Address 
{
  public string Line1 { get; set; }
  public string Line2 { get; set; }
  public string Town { get; set; }
  public string County { get; set; }
  public string Postcode { get; set; }

}

public enum AddressType
{
   HOME,
   WORK
}

Its time to call our enum validor for the address type in customer class.

public class CustomerValidator : AbstractValidator<Customer>
{
    public CustomerValidator()
   {
     RuleFor(x => x.type).SetValidator(new EnumValidator<AddressType>("errormessage");
  }
}

To expound on the performance scaling specifically regarding Timo/Matt Jenkins method: Consider the following code:

//System.Diagnostics - Stopwatch
//System - ConsoleColor
//System.Linq - Enumerable
Stopwatch myTimer = Stopwatch.StartNew();
int myCyclesMin = 0;
int myCyclesCount = 10000000;
long myExt_IsDefinedTicks;
long myEnum_IsDefinedTicks;
foreach (int lCycles in Enumerable.Range(myCyclesMin, myCyclesMax))
{
    Console.WriteLine(string.Format("Cycles: {0}", lCycles));

    myTimer.Restart();
    foreach (int _ in Enumerable.Range(0, lCycles)) { ConsoleColor.Green.IsDefined(); }
    myExt_IsDefinedTicks = myTimer.ElapsedTicks;

    myTimer.Restart();
    foreach (int _ in Enumerable.Range(0, lCycles)) { Enum.IsDefined(typeof(ConsoleColor), ConsoleColor.Green); }
    myEnum_IsDefinedTicks = myTimer.E

    Console.WriteLine(string.Format("object.IsDefined() Extension Elapsed: {0}", myExt_IsDefinedTicks.ToString()));
    Console.WriteLine(string.Format("Enum.IsDefined(Type, object): {0}", myEnum_IsDefinedTicks.ToString()));
    if (myExt_IsDefinedTicks == myEnum_IsDefinedTicks) { Console.WriteLine("Same"); }
    else if (myExt_IsDefinedTicks < myEnum_IsDefinedTicks) { Console.WriteLine("Extension"); }
    else if (myExt_IsDefinedTicks > myEnum_IsDefinedTicks) { Console.WriteLine("Enum"); }
}

Output starts out like the following:

Cycles: 0
object.IsDefined() Extension Elapsed: 399
Enum.IsDefined(Type, object): 31
Enum
Cycles: 1
object.IsDefined() Extension Elapsed: 213654
Enum.IsDefined(Type, object): 1077
Enum
Cycles: 2
object.IsDefined() Extension Elapsed: 108
Enum.IsDefined(Type, object): 112
Extension
Cycles: 3
object.IsDefined() Extension Elapsed: 9
Enum.IsDefined(Type, object): 30
Extension
Cycles: 4
object.IsDefined() Extension Elapsed: 9
Enum.IsDefined(Type, object): 35
Extension

This seems to indicate there is a steep setup cost for the static hashset object (in my environment, approximately 15-20ms. Reversing which method is called first doesn't change that the first call to the extension method (to set up the static hashset) is quite lengthy. Enum.IsDefined(typeof(T), object) is also longer than normal for the first cycle, but, interestingly, much less so.

Based on this, it appears Enum.IsDefined(typeof(T), object) is actually faster until lCycles = 50000 or so.

I'm unsure why Enum.IsDefined(typeof(T), object) gets faster at both 2 and 3 lookups before it starts rising. Clearly there's some process going on internally as object.IsDefined() also takes markedly longer for the first 2 lookups before settling in to be bleeding fast.

Another way to phrase this is that if you need to lots of lookups with any other remotely long activity (perhaps a file operation like an open) that will add a few milliseconds, the initial setup for object.IsDefined() will be swallowed up (especially if async) and become mostly unnoticeable. At that point, Enum.IsDefined(typeof(T), object) takes roughly 5x longer to execute.

Basically, if you don't have literally thousands of calls to make for the same Enum, I'm not sure how hashing the contents is going to save you time over your program execution. Enum.IsDefined(typeof(T), object) may have conceptual performance problems, but ultimately, it's fast enough until you need it thousands of times for the same enum.

As an interesting side note, implementing the ValueCache as a hybrid dictionary yields a startup time that reaches parity with Enum.IsDefined(typeof(T), object) within ~1500 iterations. Of course, using a HashSet passes both at ~50k.

So, my advice: If your entire program is validating the same enum (validating different enums causes the same level of startup delay, once for each different enum) less than 1500 times, use Enum.IsDefined(typeof(T), object). If you're between 1500 and 50k, use a HybridDictionary for your hashset, the initial cache populate is roughly 10x faster. Anything over 50k iterations, HashSet is a pretty clear winner.

Also keep in mind that we are talking in Ticks. In .Net a 10,000 ticks is 1 ms.

For full disclosure I also tested List as a cache, and it's about 1/3 the populate time as hashset, however, for any enum over 9 or so elements, it's way slower than any other method. If all your enums are less than 9 elements, (or smaller yet) it may be the fastest approach.

The cache defined as a HybridDictionary (yes, the keys and values are the same. Yes, it's quite a bit harder to read than the simpler answers referenced above):

//System.Collections.Specialized - HybridDictionary
private static class EnumHybridDictionaryValueCache<T> where T : Enum
        {
            static T[] enumValues = (T[])Enum.GetValues(typeof(T));

            static HybridDictionary PopulateDefinedValues()
            {
                HybridDictionary myDictionary = new HybridDictionary(enumValues.Length);
                foreach (T lEnumValue in enumValues)
                {
                    //Has to be unique, values are actually based on the int value. Enums with multiple aliases for one value will fail without checking.
                    //Check implicitly by using assignment.
                    myDictionary[lEnumValue] = lEnumValue;
                }
                return myDictionary;
            }

            public static readonly HybridDictionary DefinedValues = PopulateDefinedValues();
        }

I found this link that answers it quite well. It uses:

(ENUMTYPE)Enum.ToObject(typeof(ENUMTYPE), INT)
Related