Updated on

Converting a string to an enum in C# is a call to Enum.Parse<T>() or Enum.TryParse<T>(). Converting an int to an enum is a cast. That asymmetry is the whole subject: one direction goes through a method that can reject bad input, and the other goes through a cast that cannot.

An enum is a set of named constants over an integral type, int unless we say otherwise. Because the value already is an int at runtime, casting one in costs nothing and checks nothing.

So the useful question is not how to convert, which is one line either way. It is how to find out whether the value we just produced is a member of the enum at all.

To download the source code for this article, you can visit our GitHub repository.

Creating an Example Enum

Let’s first create a simple enum, which we can use for the rest of the article:

public enum WeekDay
{
    Monday,
    Tuesday,
    Wednesday,
    Thursday,
    Friday,
    Saturday,
    Sunday
}

How Do We Convert a String to an Enum in C#?

Converting a string to an enum in C# is a call to Enum.Parse<T>() or Enum.TryParse<T>(). Both match the string against the member names declared in the enum.

Enum.Parse<WeekDay>("Saturday") returns the member directly. The generic overload needs no typeof() argument and no cast back to the enum type, which is the only difference between it and the older Enum.Parse(typeof(WeekDay), value) form that most examples still show.

Casing matters by default. Enum.Parse<WeekDay>("saturday") throws an ArgumentException, and passing true as the second argument makes the match case-insensitive.

Enum.TryParse<WeekDay>(value, out var day) is the version to reach for when the string came from somewhere we do not control. It returns false instead of throwing, so a bad query string or a stale configuration value costs a branch rather than an exception.

One surprise sits underneath both calls. A string of digits is matched as a number, not as a name, so "42" parses successfully even though no member has that value.

That split between a throwing call and a reporting one is exactly how Parse and TryParse behave across the other .NET types. Let’s convert a string we control:

var inputString = "Saturday";
var inputAsEnum = Enum.Parse<WeekDay>(inputString);

This will fail though if the casing doesn’t match exactly.

To have it more robust, we can add a true argument at the end so the case is ignored:

var inputString = "SaTurDaY";
var inputAsEnum = Enum.Parse<WeekDay>(inputString, true);

How Do We Convert an Int to an Enum in C#?

Converting an int to an enum in C# is a cast, (WeekDay)6. There is no method to call, because an enum is its underlying integral type at runtime, and that type is int unless the declaration says otherwise.

Members number from zero upward when we do not assign values ourselves. Monday is 0 and Sunday is 6, so (WeekDay)6 gives us Sunday.

The cast never fails. (WeekDay)42 runs and hands back a WeekDay whose underlying value is 42 and whose name is nothing at all, because no member declares it. Printing that value prints the number.

That is the whole risk in this direction. A name that matches nothing gives us an exception at the point of conversion. An int that matches nothing gives us a value that looks like a WeekDay, passes through every method that takes one, and only misbehaves in whatever switch or lookup eventually receives it.

So an int arriving from outside our code is cast and then checked, never cast alone.

First, let’s cast an int that does name a member:

var inputInt = 6;
WeekDay intAsEnum = (WeekDay)inputInt;

As internally the enum starts counting at 0, the value of intAsEnum is Sunday.

Now let’s cast one that names nothing at all:

var undefined = (WeekDay)42;

Console.WriteLine(undefined);        // 42
Console.WriteLine(Enum.IsDefined(undefined));  // False

Nothing throws, and nothing warns us. The value simply carries a number that no member of WeekDay declares, which is exactly what the next section exists to catch.

How Do We Check if an Int or String Is a Valid Enum Value?

Two calls answer this, and they answer it for different inputs. Enum.TryParse<T>() takes a string and returns false when it cannot match one. Enum.IsDefined<T>() takes a value and returns false when no member declares it.

For an int, the order is cast first and check second: Enum.IsDefined((WeekDay)inputInt). The generic overload takes the enum value itself rather than a Type and an object, so nothing is boxed on the way in.

For a string, Enum.TryParse<T>() is not enough on its own. A numeric string skips name matching entirely, so "42" returns true and hands back an undefined value. Passing the parsed result to Enum.IsDefined() closes that gap in one more line.

The pairing is what to remember. Convert first, then ask whether the result exists, and treat anything that did not come from our own code as untrusted regardless of which direction it arrived in.

Let’s run both checks side by side:

var inputInt = 2;
var inputString = "Tuesday";
var isEnumIntParsed = Enum.IsDefined((WeekDay)inputInt);
var isEnumStringParsed = Enum.TryParse(inputString, true, out WeekDay weekDay);

The second argument in Enum.TryParse denotes whether it is case sensitive, while the out argument weekDay is the parsed enum, if parsing succeeds.

That second check is not the whole story though. Enum.TryParse matches a string of digits against the enum’s numeric values rather than against its member names, so "42" comes back true and leaves us holding a WeekDay that no member declares. Passing the parsed value to Enum.IsDefined is what closes the gap, and if the input is genuinely meant to be a number rather than a name, the honest fix is to convert a string to an int first and validate that.

That pairing is what the reference itself recommends. Microsoft’s documentation for Enum.Parse puts it this way: “If this behavior is undesirable, call the IsDefined method to ensure that a particular string representation of an integer is actually a member of enumType.”

How Do We Validate a Flags Enum Value?

A flags enum breaks the ordinary check, because a valid combination is not a declared member. UserType.Customer | UserType.Driver is 3, no member declares 3, and Enum.IsDefined() returns false for it.

Microsoft’s own documentation works around this by looking for a comma in the value’s string representation, since a combined value formats as “Customer, Driver”. It works, and it allocates a string on every call to answer a question about bits.

A mask compares the bits directly instead. We combine every declared flag into one value, then test whether the value we parsed has any bit that the mask does not.

Zero needs a decision either way. (UserType)0 passes a mask test whether or not the enum declares a zero member, so giving every flags enum an explicit None = 0 keeps the check honest and keeps the default value meaningful.

The last case to consider is one of the enum flags, which we discussed in an earlier article. Converting an int to an enum having a [Flags] attribute works as usual and gives correct results:

[Flags]
public enum UserType
{
    None = 0,
    Customer = 1,
    Driver = 2,
    Admin = 4,
}

var inputInt = 3;
var parsedEnum = (UserType)inputInt; //parsedEnum equals UserType.Driver | UserType.Customer

However, we can’t just use Enum.IsDefined to check whether it succeeded, as the parsed value might not appear in the enum, but rather can be a collection of multiple values. So, let’s see what is the correct way to check whether it succeeded:

var isEnumIntParsed = Enum.IsDefined(parsedEnum) || parsedEnum.ToString().Contains(",");

This method uses the string representation of the parsedEnum and checks whether there is a comma, which means the conversion was successful and that it is a combination of values. In case it is a single value, for instance when converting the integer 2, the first term in the statement already gives a true value.

However, in our example, the inputInt (3) (2+1, i.e. UserType.Driver + UserType.Customer) is converted to an enum, but because 3 doesn’t appear in UserType the Enum.IsDefined method will return false, hence the extra check for the comma in the parsed string representation.

That answer is correct, and it builds a string to reach it. The bitwise alternative asks the same question of the bits themselves:

var allFlags = (UserType)0;

foreach (var flag in Enum.GetValues<UserType>())
{
    allFlags |= flag;
}

var parsedEnum = (UserType)inputInt;
var isValidCombination = (parsedEnum & ~allFlags) == 0;

Here allFlags comes out as 7, so 0 through 7 are combinations of declared flags and 8 is not. Because this enum declares None = 0, the mask and the comma check agree on every value from 0 to 8; drop that member and they part company at 0, where the mask still says yes and the comma check says no.

How Do We Pick the Right Enum Conversion in C#?

Three things decide which call we make: what we are holding, whether we trust where it came from, and whether the enum carries the [Flags] attribute.

A name we wrote ourselves parses with Enum.Parse<T>(), and an exception on a value we control is a bug worth throwing. A name from a request, a file, or a database goes through Enum.TryParse<T>(), because throwing on every malformed input is a cost paid for nothing.

An int is always a cast, and the only real question is what validates it afterwards. A plain enum uses Enum.IsDefined<T>(). A flags enum uses a mask over its declared flags.

None of this needs a library, an extension method, or a converter. The calls in the table below cover every combination, and the mistake worth avoiding is trusting a value because the conversion raised no error.

We haveWe wantCallWhen the value is not a member
A name we controlThe enum memberEnum.Parse<WeekDay>(name)Throws ArgumentException
A name in any casingThe enum memberEnum.Parse<WeekDay>(name, true)Throws ArgumentException
A name from outsideThe enum member, or nothingEnum.TryParse<WeekDay>(name, out var day)Returns false, except for a numeric string
A name from outside, any casingThe enum member, or nothingEnum.TryParse<WeekDay>(name, true, out var day)Returns false, except for a numeric string
An intThe enum member(WeekDay)numberSucceeds anyway, with an undefined value
An enum value to verifyA yes or noEnum.IsDefined((WeekDay)number)Returns false
A [Flags] value to verifyA yes or noMask test, see the flags sectionReturns false

Conclusion

Converting to an enum is one line in either direction, and validating the result is the line that matters. We have also seen how we can check whether it was a valid integer or string, and how even for enum flags we can do this check. The opposite direction has an answer of its own: when the value has to leave our code as text, we serialize an enum as a string.

Tested with .NET 10.