Is there a way to check if int is legal enum in C#?

C#Enums

C# Problem Overview


I've read a few SO posts and it seems most basic operation is missing.

public enum LoggingLevel
{
    Off = 0,
    Error = 1,
    Warning = 2,
    Info = 3,
    Debug = 4,
    Trace = 5
};

if (s == "LogLevel")
{
    _log.LogLevel = (LoggingLevel)Convert.ToInt32("78");
    _log.LogLevel = (LoggingLevel)Enum.Parse(typeof(LoggingLevel), "78");
    _log.WriteDebug(_log.LogLevel.ToString());
}

This causes no exceptions, it's happy to store 78. Is there a way to validate a value going into an enum?

C# Solutions


Solution 1 - C#

Check out Enum.IsDefined

Usage:

if(Enum.IsDefined(typeof(MyEnum), value))
    MyEnum a = (MyEnum)value; 

This is the example from that page:

using System;    
[Flags] public enum PetType
{
   None = 0, Dog = 1, Cat = 2, Rodent = 4, Bird = 8, Reptile = 16, Other = 32
};

public class Example
{
   public static void Main()
   {
      object value;     
      // Call IsDefined with underlying integral value of member.
      value = 1;
      Console.WriteLine("{0}: {1}", value, Enum.IsDefined(typeof(PetType), value));
      // Call IsDefined with invalid underlying integral value.
      value = 64;
      Console.WriteLine("{0}: {1}", value, Enum.IsDefined(typeof(PetType), value));
      // Call IsDefined with string containing member name.
      value = "Rodent";
      Console.WriteLine("{0}: {1}", value, Enum.IsDefined(typeof(PetType), value));
      // Call IsDefined with a variable of type PetType.
      value = PetType.Dog;
      Console.WriteLine("{0}: {1}", value, Enum.IsDefined(typeof(PetType), value));
      value = PetType.Dog | PetType.Cat;
      Console.WriteLine("{0}: {1}", value, Enum.IsDefined(typeof(PetType), value));
      // Call IsDefined with uppercase member name.      
      value = "None";
      Console.WriteLine("{0}: {1}", value, Enum.IsDefined(typeof(PetType), value));
      value = "NONE";
      Console.WriteLine("{0}: {1}", value, Enum.IsDefined(typeof(PetType), value));
      // Call IsDefined with combined value
      value = PetType.Dog | PetType.Bird;
      Console.WriteLine("{0:D}: {1}", value, Enum.IsDefined(typeof(PetType), value));
      value = value.ToString();
      Console.WriteLine("{0:D}: {1}", value, Enum.IsDefined(typeof(PetType), value));
   }
}

The example displays the following output:

//       1: True
//       64: False
//       Rodent: True
//       Dog: True
//       Dog, Cat: False
//       None: True
//       NONE: False
//       9: False
//       Dog, Bird: False

Solution 2 - C#

The above solutions do not deal with [Flags] situations.

My solution below may have some performance issues (I'm sure one could optimise in various ways) but essentially it will always prove whether an enum value is valid or not.

It relies on three assumptions:

  • Enum values in C# are only allowed to be int, absolutely nothing else
  • Enum names in C# must begin with an alphabetic character
  • No valid enum name can being with a minus sign: -

Calling ToString() on an enum returns either the int value if no enum (flag or not) is matched. If an allowed enum value is matched, it will print the name of the match(es).

So:

[Flags]
enum WithFlags
{
    First = 1,
    Second = 2,
    Third = 4,
    Fourth = 8
}

((WithFlags)2).ToString() ==> "Second"
((WithFlags)(2 + 4)).ToString() ==> "Second, Third"
((WithFlags)20).ToString() ==> "20"

With these two rules in mind we can assume that if the .NET Framework does its job correctly that any calls to a valid enum's ToString() method will result in something that has an alphabetic character as its first character:

public static bool IsValid<TEnum>(this TEnum enumValue)
    where TEnum : struct
{
    var firstChar = enumValue.ToString()[0];
	return (firstChar < '0' || firstChar > '9') && firstChar != '-';
}

One could call it a "hack", but the advantages are that by relying on Microsoft's own implementation of Enum and C# standards, you're not relying on your own potentially buggy code or checks. In situations where performance is not exceptionally critical, this will save a lot of nasty switch statements or other checks!

Edit

Thanks to @ChaseMedallion for pointing out that my original implementation did not support negative values. This has been remedied and tests provided.

And the tests to back it up:

[TestClass]
public class EnumExtensionsTests
{
	[Flags]
	enum WithFlags
	{
		First = 1,
		Second = 2,
		Third = 4,
		Fourth = 8
	}

	enum WithoutFlags
	{
		First = 1,
		Second = 22,
		Third = 55,
		Fourth = 13,
		Fifth = 127
	}

	enum WithoutNumbers
	{
		First, // 1
		Second, // 2
		Third, // 3
		Fourth // 4
	}

	enum WithoutFirstNumberAssigned
	{
		First = 7,
		Second, // 8
		Third, // 9
		Fourth // 10
	}


	enum WithNagativeNumbers
	{
		First = -7,
		Second = -8,
		Third = -9,
		Fourth = -10
	}

	[TestMethod]
	public void IsValidEnumTests()
	{
		Assert.IsTrue(((WithFlags)(1 | 4)).IsValid());
		Assert.IsTrue(((WithFlags)(1 | 4)).IsValid());
		Assert.IsTrue(((WithFlags)(1 | 4 | 2)).IsValid());
		Assert.IsTrue(((WithFlags)(2)).IsValid());
		Assert.IsTrue(((WithFlags)(3)).IsValid());
		Assert.IsTrue(((WithFlags)(1 + 2 + 4 + 8)).IsValid());

		Assert.IsFalse(((WithFlags)(16)).IsValid());
		Assert.IsFalse(((WithFlags)(17)).IsValid());
		Assert.IsFalse(((WithFlags)(18)).IsValid());
		Assert.IsFalse(((WithFlags)(0)).IsValid());

		Assert.IsTrue(((WithoutFlags)1).IsValid());
		Assert.IsTrue(((WithoutFlags)22).IsValid());
		Assert.IsTrue(((WithoutFlags)(53 | 6)).IsValid());	 // Will end up being Third
		Assert.IsTrue(((WithoutFlags)(22 | 25 | 99)).IsValid()); // Will end up being Fifth
		Assert.IsTrue(((WithoutFlags)55).IsValid());
		Assert.IsTrue(((WithoutFlags)127).IsValid());

		Assert.IsFalse(((WithoutFlags)48).IsValid());
		Assert.IsFalse(((WithoutFlags)50).IsValid());
		Assert.IsFalse(((WithoutFlags)(1 | 22)).IsValid());
		Assert.IsFalse(((WithoutFlags)(9 | 27 | 4)).IsValid());

		Assert.IsTrue(((WithoutNumbers)0).IsValid());
		Assert.IsTrue(((WithoutNumbers)1).IsValid());
		Assert.IsTrue(((WithoutNumbers)2).IsValid());
		Assert.IsTrue(((WithoutNumbers)3).IsValid());
		Assert.IsTrue(((WithoutNumbers)(1 | 2)).IsValid());	// Will end up being Third
		Assert.IsTrue(((WithoutNumbers)(1 + 2)).IsValid());	// Will end up being Third

		Assert.IsFalse(((WithoutNumbers)4).IsValid());
		Assert.IsFalse(((WithoutNumbers)5).IsValid());
		Assert.IsFalse(((WithoutNumbers)25).IsValid());
		Assert.IsFalse(((WithoutNumbers)(1 + 2 + 3)).IsValid());

		Assert.IsTrue(((WithoutFirstNumberAssigned)7).IsValid());
		Assert.IsTrue(((WithoutFirstNumberAssigned)8).IsValid());
		Assert.IsTrue(((WithoutFirstNumberAssigned)9).IsValid());
		Assert.IsTrue(((WithoutFirstNumberAssigned)10).IsValid());

		Assert.IsFalse(((WithoutFirstNumberAssigned)11).IsValid());
		Assert.IsFalse(((WithoutFirstNumberAssigned)6).IsValid());
		Assert.IsFalse(((WithoutFirstNumberAssigned)(7 | 9)).IsValid());
		Assert.IsFalse(((WithoutFirstNumberAssigned)(8 + 10)).IsValid());

		Assert.IsTrue(((WithNagativeNumbers)(-7)).IsValid());
		Assert.IsTrue(((WithNagativeNumbers)(-8)).IsValid());
		Assert.IsTrue(((WithNagativeNumbers)(-9)).IsValid());
		Assert.IsTrue(((WithNagativeNumbers)(-10)).IsValid());
		Assert.IsFalse(((WithNagativeNumbers)(-11)).IsValid());
		Assert.IsFalse(((WithNagativeNumbers)(7)).IsValid());
		Assert.IsFalse(((WithNagativeNumbers)(8)).IsValid());
	}
}

Solution 3 - C#

The canonical answer would be Enum.IsDefined, but that is a: a bit slow if used in a tight loop, and b: not useful for [Flags] enums.

Personally, I'd stop worrying about that, and just switch appropriately, remembering:

  • if it is OK not to recognise everything (and just not do anything), then don't add a default: (or have an empty default: explaining why)
  • if there is a sensible default behaviour, put that in the default:
  • otherwise, handle the ones you know about and throw an exception for the rest:

Like so:

switch(someflag) {
    case TriBool.Yes:
        DoSomething();
        break;
    case TriBool.No:
        DoSomethingElse();
        break;
    case TriBool.FileNotFound:
        DoSomethingOther();
        break;
    default:
        throw new ArgumentOutOfRangeException("someflag");
}

Solution 4 - C#

Use:

Enum.IsDefined ( typeof ( Enum ), EnumValue );

Solution 5 - C#

Solution 6 - C#

In order to deal with [Flags] you can also use this solution from C# Cookbook:

First, add a new ALL value to your enum:

[Flags]
enum Language
{
    CSharp = 1, VBNET = 2, VB6 = 4, 
    All = (CSharp | VBNET | VB6)
}

Then, check if the value is in ALL:

public bool HandleFlagsEnum(Language language)
{
    if ((language & Language.All) == language)
    {
        return (true);
    }
    else
    {
        return (false);
    }
}

Solution 7 - C#

One way to do would be to rely on casting and enum to string conversion. When casting int to an Enum type the int is either converted to a corresponding enum value or the resulting enum just contains int as a value if enum value is not defined for the int.

enum NetworkStatus{
  Unknown=0,
  Active,
  Slow
}

int statusCode=2;
NetworkStatus netStatus = (NetworkStatus) statusCode;
bool isDefined = netStatus.ToString() != statusCode.ToString();

Not tested for any edge cases.

Solution 8 - C#

As the others said, Enum.IsDefined returns false even if you have a valid combination of bit flags for an enum decorated with the FlagsAttribute.

Sadly, the only way to create a method returning true for valid bit flags is a bit lengthy:

public static bool ValidateEnumValue<T>(T value) where T : Enum
{
    // Check if a simple value is defined in the enum.
    Type enumType = typeof(T);
    bool valid = Enum.IsDefined(enumType, value);
    // For enums decorated with the FlagsAttribute, allow sets of flags.
    if (!valid && enumType.GetCustomAttributes(typeof(FlagsAttribute), false)?.Any() == true)
    {
        long mask = 0;
        foreach (object definedValue in Enum.GetValues(enumType))
            mask |= Convert.ToInt64(definedValue);
        long longValue = Convert.ToInt64(value);
        valid = (mask & longValue) == longValue;
    }
    return valid;
}

You may want to cache the results of GetCustomAttribute in a dictionary:

private static readonly Dictionary<Type, bool> _flagEnums = new Dictionary<Type, bool>();
public static bool ValidateEnumValue<T>(T value) where T : Enum
{
    // Check if a simple value is defined in the enum.
    Type enumType = typeof(T);
    bool valid = Enum.IsDefined(enumType, value);
    if (!valid)
    {
        // For enums decorated with the FlagsAttribute, allow sets of flags.
        if (!_flagEnums.TryGetValue(enumType, out bool isFlag))
        {
            isFlag = enumType.GetCustomAttributes(typeof(FlagsAttribute), false)?.Any() == true;
            _flagEnums.Add(enumType, isFlag);
        }
        if (isFlag)
        {
            long mask = 0;
            foreach (object definedValue in Enum.GetValues(enumType))
                mask |= Convert.ToInt64(definedValue);
            long longValue = Convert.ToInt64(value);
            valid = (mask & longValue) == longValue;
        }
    }
    return valid;
}

Note that the code above uses the new Enum constraint on T which is only available since C# 7.3. You need to pass an object value in older versions and call GetType() on it.

Solution 9 - C#

I know this is an old question, but I ran into this today, and I wanted to expand on Josh Comley's answer (https://stackoverflow.com/a/23177585/3403999)

There's a couple of wrong assumptions in Josh's answer that I wanted to address:

  1. It assumes that the '-' is always the negative sign. I don't know if there is any cultures that use a different sign, but .Net certainly allows for it in the NumberFormatInfo (https://docs.microsoft.com/en-us/dotnet/api/system.globalization.numberformatinfo.negativesign?view=net-5.0). About the only one I can think of that might be common is the parenthesis, ie (1) == -1.
  2. Enum members have to start with an alphabetic character. Specifically, I know you can use an underscore as the first char. IE, enum MyEnum { _One = 1 } is valid.
  3. Not really sure this exactly wrong, but it made the assumption that anything outside the range of '0' to '9' and '-' is a valid alphabetic character. It seemed like a bad assumption cause there are control characters outside that range that would return true - albeit, I don't think you can get those control characters into an enum member name without it throwing a compile error.

Anyway, here's my updated solution:

public static bool IsValid<TEnum>(this TEnum value) where TEnum : System.Enum
{
    char first = value.ToString()[0];
    return (char.IsLetter(first) || first == '_');
}

I did discover that you can use Unicode letters from other languages in enum member names (https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/inside-a-program/identifier-names). My solution still passes in this regard. I tested with the following enum: enum MyEnum { \u05D0 }. The enum compiled, and the IsValid returned true.

I was curious what kind of performance hit you'd take going this route vs using a static helper class with a HashSet that is filled with Enum.GetValues(typeof(TEnum)) where you check to see if the HashSet contains the enum value. The thought being that both Enum.GetValues and Enum.IsDefined are just wrappers around expensive Reflection hits, so you do the Reflection once with GetValues, cache the results, and then just check the HashSet going forward.

I ran a fairly simple test with a StopWatch and Random that would generate valid & invalid enum values, and then I ran them through 3 different methods: the ToString method, the GetValues HashSet method, and the IsDefined method. I had them do each method int.MaxValue times. The results:

  • ToString averaged about 2 minutes every time I ran it 2 billion times.
  • GetValues HashSet about 50 seconds every time I ran it 2 billion times.
  • IsDefined about 5 minutes every time I ran it 2 billion times.

So all the solutions recommending IsDefined are probably a bad idea if performance is a concern, or your doing a loop. If you are only using it somehow validate user input on single instances, it probably doesn't matter.

For the HashSet, it's a small performance hit for each different enum you run through it (cause the first time a new enum type gets ran through generates a new static HashSet). Not scientific, but it seemed my break even point on my PC was about 200k to 300k runs for a single enum before it started out performing using the ToString method.

The ToString method, while not the fastest had the added benefit of handling Flags enums that neither the IsDefined nor HashSet accommodate.

If performance really is a concern, don't use any of these 3 methods. Instead write a method that validates on a specific enum optimized to that enum.

Also note that my tests were with relatively small enums (5 or so elements). I don't know how performance between ToString vs HashSet once you start getting into larger enums.

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
Questionchar mView Question on Stackoverflow
Solution 1 - C#SwDevMan81View Answer on Stackoverflow
Solution 2 - C#joshcomleyView Answer on Stackoverflow
Solution 3 - C#Marc GravellView Answer on Stackoverflow
Solution 4 - C#n535View Answer on Stackoverflow
Solution 5 - C#driisView Answer on Stackoverflow
Solution 6 - C#Mario LevreroView Answer on Stackoverflow
Solution 7 - C#maulik13View Answer on Stackoverflow
Solution 8 - C#RayView Answer on Stackoverflow
Solution 9 - C#B.O.B.View Answer on Stackoverflow