Richard C.
—A common task in C# is to get an enum value for a given integer or return the integer value of an enum for one of its values, or its name as a string.
For example, in the following enum, how would you print the string “Credit”, given the value 1
from a customer?
public enum PaymentMethod { Credit = 1, Cash = 2, EFT = 3 }
An enum value (the Credit = 1
part of PaymentMethod
) has three parts:
PaymentMethod
)1
)Credit
)We need five different functions to convert between these three types. Here they are in one example using .NET 8:
using System; public enum PaymentMethod { Credit = 1, Cash = 2, EFT = 3 } public class Program { public static void Main() { Console.WriteLine(GetEnumValueFromInt(1)); // Credit Console.WriteLine(GetEnumNameFromInt(1)); // "Credit", but this is a string Console.WriteLine(GetIntFromEnumValue(PaymentMethod.Cash)); // 2 Console.WriteLine(GetIntFromEnumName("Cash")); // 2 Console.WriteLine(GetEnumNameFromEnumValue(PaymentMethod.EFT)); // "EFT" } public static PaymentMethod GetEnumValueFromInt(int value) { return (PaymentMethod)value; } public static string GetEnumNameFromInt(int value) { return Enum.GetName((PaymentMethod)value); } public static int GetIntFromEnumValue(PaymentMethod paymentMethod) { return (int)paymentMethod; } public static int GetIntFromEnumName(string name) { return (int)Enum.Parse(typeof(PaymentMethod), name); } public static string GetEnumNameFromEnumValue(PaymentMethod paymentMethod) { return Enum.GetName(paymentMethod); } }
As you can see in the above code, it’s trivial to cast an enum value back and forth to an int. This is because an enum value is basically an int in .NET. However, more work is required in the methods using an enum name, because an enum value is not stored as a string in memory.
The methods above will fail if you pass values that are not in the range of the enum. However, your program might not crash — it might just continue running with completely incorrect values.
To fix this you should add an undefined
enum element, to avoid returning a null value that might crash your program, and validate your inputs. Here’s an example:
using System; public enum PaymentMethod { Undefined = -1, Credit = 1, Cash = 2, EFT = 3 } public class Program { public static void Main() { Console.WriteLine(GetEnumValueFromInt(5)); // Undefined Console.WriteLine(GetEnumNameFromInt(5)); // Undefined Console.WriteLine(GetIntFromEnumName("Bitcoin")); //-1 } public static PaymentMethod GetEnumValueFromInt(int value) { if (Enum.IsDefined(typeof(PaymentMethod), value)) return (PaymentMethod)value; return PaymentMethod.Undefined; } public static string GetEnumNameFromInt(int value) { if (Enum.IsDefined(typeof(PaymentMethod), value)) return Enum.GetName((PaymentMethod)value); return Enum.GetName(PaymentMethod.Undefined); } public static int GetIntFromEnumName(string name) { if (Enum.IsDefined(typeof(PaymentMethod), name)) return (int)Enum.Parse(typeof(PaymentMethod), name); return (int)PaymentMethod.Undefined; } }
An enum is an int by default, but it doesn’t have to be. An enum can use any integral type, like long or byte:
public enum PaymentMethod : short { Credit = 1, Cash = 2, EFT = 3 }
This causes a problem with our conversion methods. They are set to use int. To handle every type of enum, we would have to rewrite all five methods to use long, short, byte, etc. Unfortunately, C# has no base type Integral
that all integral types inherit from. This is because ints and longs are primitive types, not object types.
You might think you could use a long for every parameter in your functions because a long can fit any other integral. But unfortunately, .NET cannot automatically convert between an integral and an enum if the types aren’t identical. You’ll get the error System.ArgumentException: Enum underlying type and the object must be same type or object must be a String
.
If you want to write a single set of functions that convert an enum of any type, you’ll have to use reflection:
using System; public enum PaymentMethod : short { Undefined = 0, Credit = 1, Cash = 2, EFT = 3 } public class Program { public static void Main() { Console.WriteLine(GetEnumNameFromInt(1)); // Credit } public static string GetEnumNameFromInt(long value) { Type enumType = typeof(PaymentMethod); Type underlyingIntegralType = Enum.GetUnderlyingType(enumType); try { object convertedValue = Convert.ChangeType(value, underlyingIntegralType); if (Enum.IsDefined(enumType, convertedValue)) return Enum.GetName(enumType, convertedValue); } catch (OverflowException) { // Handle the case where the value is out of range for the underlying type } catch (InvalidCastException) { // Handle the case where the conversion is not possible } return Enum.GetName(enumType, PaymentMethod.Undefined); } }
First, we get the integral type of the enum with GetUnderlyingType
, then we use Convert.ChangeType
to convert our parameter to the same type as the enum. Now we can use it as we did in the previous methods.
You can define an enum without specifying the integer values of each item. By default, they start at 0
:
public enum PaymentMethod { Undefined, Credit, Cash, EFT }
Don’t do this. Always give each item a number yourself.
There were old reports of different systems having the same enum values with different integer values. Although this is unlikely in modern .NET, it’s still possible for a programmer in your team to accidentally insert a value into the middle of an enum, breaking all the mappings of the subsequent enums, and causing errors when serializing your enums to ints in XML, JSON, or YAML documents you send to clients.
Tasty treats for web developers brought to you by Sentry. Get tips and tricks from Wes Bos and Scott Tolinski.
SEE EPISODESConsidered “not bad” by 4 million developers and more than 100,000 organizations worldwide, Sentry provides code-level observability to many of the world’s best-known companies like Disney, Peloton, Cloudflare, Eventbrite, Slack, Supercell, and Rockstar Games. Each month we process billions of exceptions from the most popular products on the internet.
Here’s a quick look at how Sentry handles your personal information (PII).
×We collect PII about people browsing our website, users of the Sentry service, prospective customers, and people who otherwise interact with us.
What if my PII is included in data sent to Sentry by a Sentry customer (e.g., someone using Sentry to monitor their app)? In this case you have to contact the Sentry customer (e.g., the maker of the app). We do not control the data that is sent to us through the Sentry service for the purposes of application monitoring.
Am I included?We may disclose your PII to the following type of recipients:
You may have the following rights related to your PII:
If you have any questions or concerns about your privacy at Sentry, please email us at [email protected].
If you are a California resident, see our Supplemental notice.