String Enums in C# — When, Why, and How?
Today, we'll talk about enum values: do they matter and if they do, are strings a better choice than numbers to represent them? We'll also see how to use string enums in C#.
- Values of enum members are generally not important. However, in certain cases, they can be useful.
- Strings often represent enums better than numbers. Unfortunately, C# does not natively support string enums.
- I've created a package called StrEnum that helps creating string enums in C#. StrEnum supports EF Core, ASP.NET Core, Dapper, and JSON.
1. Enum values are often secondary
When dealing with enums in C#, you generally only care about the names of their members and not about their numeric values. That is because the purpose of enums is to model a choice — and all you need for that is the names of the options to choose from:
As long as you only refer to the options by name, you don't care about their underlying numeric values. Do you really need to know that
Winter's value is 3 to say that it's not equal to
2. But sometimes they are useful
There are three cases where numeric values of enum members are important.
2.1. Enum values carry meaning
The most obvious case is using an enum to group a set of related constants. You give each number a name and put it under the same enum roof with the other similar members:
In this case, the values of the members are important as they carry meaning so they have to be specified explicitly.
2.2. You are creating a multi-choice enum
An enum can also represent a combination of choices. Such enum must have the
[Flags] attribute applied to it and its members must act as bit fields, i.e. have the powers of two as their values:
2.3. The value of an enum crosses the app boundary
C# apps rarely run isolated or produce no side effects. It's more common for an app to store its state in a database, interact with a third-party API, or provide data to a front-end.
If the data that your app exchanges with the third parties contain enums, it's a good practice to specify the values of their members:
PostReaction travels outside of your app, all the systems that process it now need to know the meaning behind the numbers that represent the enum. The enum values become a part of the contract between your app and the third parties, so it is better to be clear and specify those values explicitly.
3. Strings as enum values
When enum values matter, using strings instead of numbers can often bring more value.
3.1. Strings carry more meaning than numbers
String values are self-descriptive. They often have meaning outside of the C# code.
Imagine that you need to track user reactions to blog posts and store them in a database. You create the following enum and assign the values to each of the items:
You complete the feature and everyone's happy: your app stores the reactions and displays the correct counters for the posts.
But then your data team decides to run analytics on users' reactions. They query the reactions table and see the following:
"What do all those Reaction numbers mean?" — they ask you. You open the definition of the
PostReaction enum and send them the name and the value pair of each reaction type.
Sometime later you are asked to add a new reaction, a
SurprisedFace. That's easy. You add a new enum member with the value of
In a few days, you receive another message from the data team, asking what that new
4 reaction is.
From now on you have an additional task to keep the data team aware of all the changes to the collection of reactions. And you rarely have just one such enum in your app. There can also be order statuses, account types, user preferences, and so on. You end up describing the values of each enum either in a reference table in your database or somewhere deep in your company's wiki. Whatever option you choose, you and your team now have to keep those tables in sync with the enums.
But what if you could do something like this:
By doing so, your C# code would still function the same but
PostReactions would be stored the following way:
The values of
PostReaction would be self-descriptive and unlike randomly-chosen numbers, they would have meaning on their own. Such values would have meaning even if the source code of your app is lost.
Unfortunately, such string enums are not supported by C#. However, there are ways to simulate them which I will cover further down in this post.
3.2. Strings are more human-friendly
What if both numbers and strings can meaningfully represent an enum? Countries are a good example. Rather than coming up with a random numeric ID per country, you can use the ISO 3166 Country Codes standard. For each country, it defines both a string and a numeric code. Ukraine, for example, has a numeric code of
840 and a three-letter code of
"UKR" to me is much more meaningful than
When choosing between numbers and strings as an underlying enum type, prefer strings. As you've seen before, strings are self-descriptive and are more human-friendly than numbers. They make data easy to read and understand which will save your team time to debug, investigate, and analyze your systems.
4. When not to use strings
There are two cases where you should prefer using numbers over strings for enums.
4.1. Performance is critical
Databases are usually faster in sorting and indexing numbers than strings. Keep this in mind, especially if you are dealing with big volumes of data.
Strings also take up more space. That can increase the amount of data transferred in and out of your app as well as the size of your data files, indexes, and backups.
Having said that, I would always start with keeping your data as human-readable and self-descriptive as possible and only optimize for speed, bandwidth, and storage if they become an issue.
4.2. Numbers are more meaningful
Sometimes, numbers represent enum values better than strings. In such cases, stick with the standard numeric enums:
5. String enums in C#
As I mentioned before, C# does not natively support string enums. Luckily, they are easy to simulate.
5.1. Introducing StrEnum
PostReaction, built with StrEnum, looks like this:
Now you can use it in the similar way you'd use a normal C# enum:
5.2. String enum limitations
Since string enums are not natively supported by C#, they have a few minor limitations compared to regular enums.
5.2.1. No [Flags] support
String enum variable can only represent a single choice. That's because C# [Flags] enums use bitwise operations to combine multiple options, and these operations do not apply to strings.
5.2.2. Cannot be used in attributes
String enums cannot be used in attributes, since attributes only accept constant expressions as arguments, and string enum members are created at runtime. Assuming
UserRole is a string enum that describes user roles, the following wouldn't compile:
5.2.3. Cannot be used as default parameter values
String enum members cannot be used as default parameter values for optional parameters; such default values can only be null. The reason is the same as in the attributes case above: string enum members are not compile-time constants.
5.2.4. Require bulkier
You can't use string enums in regular, pre C# 7
switch statements, since values for
case clauses have to be constants. In other words, the following won't compile:
But there is a workaround. You can write slightly bulkier
switch statements with the help of C# 8 property patterns and
when case guards:
You can also write concise
switch expressions like this:
Enum members are generally referenced by name and their numeric values are often secondary.
However, there are cases where numeric enum values are useful: when the enum groups constants with meaningful values, when it is a multi-choice enum, or when it travels in or out of your C# application. In all these cases it is better to specify enum values explicitly.
Strings often represent enum values better than numbers. That's because strings are self-descriptive and human-readable. I generally recommend using strings over numbers, although there are a few cases where numbers can be a better fit.
And what is your take on string enums? Have you used them in production? Do you think they are worth the additional effort? Don't hesitate to share your stories and opinions in the comments!