Many values in a program come from a fixed set of options: the status of an order, the days of the week, a user's role, the difficulty level of a game. You could store them as numbers (status = 2) or strings (status = "shipped"), but numbers are meaningless to readers and strings invite typos like "shiped" that the compiler can't catch.
An enum (enumeration) defines a type whose values are a set of named constants. OrderStatus.Shipped is clear to read, the compiler rejects misspellings, and your editor lists the valid options as you type. Behind the scenes each value is a number, so enums are as fast and compact as integers.
In this lesson you'll declare enums, use them in conditions and switch expressions, convert them to and from text and numbers safely, list every value for menus and dropdowns, and combine several options in one value with flags.
Declaring and using enums
enum OrderStatus
{
Pending,
Paid,
Shipped,
Delivered,
Cancelled
}
OrderStatus status = OrderStatus.Paid;
if (status == OrderStatus.Paid)
{
Console.WriteLine("Ready to ship");
}- Enum names and their values use PascalCase. The type name is usually singular:
OrderStatus, notOrderStatuses. - Values are written as
TypeName.Value. - By default, the first value is
0, the next1, and so on.
Numbers and explicit values
You can choose the numbers yourself, which matters when values are stored in a database or sent to another system:
enum Priority
{
Low = 1,
Normal = 5,
High = 10
}
int n = (int)Priority.High; // 10
Priority p = (Priority)5; // Priority.NormalCasting a number that doesn't match any name still works, giving an "unnamed" value such as (Priority)7. Enum.IsDefined(typeof(Priority), 7) checks whether a number is valid.
Strings, parsing and listing
status.ToString()gives the name:"Paid".Enum.Parse<OrderStatus>("Shipped")converts text to an enum and throws if it doesn't match.Enum.TryParse<OrderStatus>(text, ignoreCase: true, out var result)is the safe version for user input.Enum.GetValues<OrderStatus>()returns every value, perfect for filling a dropdown or menu.
Enums work beautifully with switch expressions:
string label = status switch
{
OrderStatus.Pending => "Waiting for payment",
OrderStatus.Shipped => "On its way",
_ => status.ToString()
};Flags
A [Flags] enum lets one value hold several options at once. Give each option a power of two, then combine with |:
[Flags]
enum Permission
{
None = 0,
Read = 1,
Write = 2,
Delete = 4
}
Permission editor = Permission.Read | Permission.Write;
bool canWrite = editor.HasFlag(Permission.Write); // trueEach option is a separate bit, so combinations never clash. ToString() on a flags value prints "Read, Write".
Adding, removing and testing flags with bitwise operators
HasFlag is the readable way to test a flag, but you'll often see the bitwise operators it's built on, and you need them to change a flags value:
Permission perms = Permission.Read;
perms |= Permission.Write; // add a flag: Read, Write
bool canWrite = (perms & Permission.Write) != 0; // test: true
perms &= ~Permission.Write; // remove a flag: Read|(or) switches a bit on, so|=adds an option.&(and) keeps only the bits both sides share, so(perms & Permission.Write) != 0asks "is Write switched on?".~(not) flips every bit, so& ~Permission.Writekeeps everything except Write.
One trap: HasFlag(Permission.None) is always true, because every value contains "no bits". Test for None with perms == Permission.None instead.
Underlying types and enums in JSON
An enum is stored as an int unless you choose another whole-number type after a colon:
enum Level : byte { Low, High }A byte enum takes one byte instead of four, which only matters when you store millions of values or must match a file format. For everyday code, leave it as int.
When you serialise an object with System.Text.Json, enums are written as numbers by default, so Level.High becomes 1. Add a JsonStringEnumConverter to write names instead:
var options = new JsonSerializerOptions();
options.Converters.Add(new JsonStringEnumConverter());
string json = JsonSerializer.Serialize(new { Status = Level.High }, options);
// {"Status":"High"}Names make JSON easier to read and stay correct if someone later reorders the enum members.
C# Types Syntax
Enum syntax
enum Size { Small, Medium, Large }
enum Code { Ok = 200, NotFound = 404 }
Size s = Size.Medium;
int n = (int)s; // 1
Size fromInt = (Size)2; // Large
string name = s.ToString(); // "Medium"
bool ok = Enum.TryParse<Size>("large", true, out var parsed);
foreach (Size v in Enum.GetValues<Size>()) { }
[Flags] enum Days { None = 0, Mon = 1, Tue = 2, Wed = 4 }
var d = Days.Mon | Days.Wed; d.HasFlag(Days.Tue);C# Types Summary
- An enum is a type with a fixed set of named constant values.
- Values default to 0, 1, 2...; you can assign explicit numbers.
- Cast with (int) and (MyEnum); check numbers with Enum.IsDefined.
- ToString gives the name; Enum.TryParse converts text safely; GetValues lists all values.
- [Flags] enums combine options with | and test them with HasFlag.
C# Types Examples
Each example shows the code, the output it produces and what each part does.
Order status with a switch expression
var orders = new (int Id, OrderStatus Status)[]
{
(101, OrderStatus.Pending),
(102, OrderStatus.Shipped),
(103, OrderStatus.Cancelled)
};
foreach (var (id, status) in orders)
{
string message = status switch
{
OrderStatus.Pending => "Waiting for payment",
OrderStatus.Paid => "Being packed",
OrderStatus.Shipped => "On its way",
OrderStatus.Delivered => "Delivered",
OrderStatus.Cancelled => "Cancelled, refund issued",
_ => "Unknown status"
};
Console.WriteLine($"#{id} {status} ({(int)status}): {message}");
}
enum OrderStatus { Pending, Paid, Shipped, Delivered, Cancelled }#101 Pending (0): Waiting for payment
#102 Shipped (2): On its way
#103 Cancelled (4): Cancelled, refund issuedLine-by-line explanation
- Line 1An array of tuples pairs each id with a status.
- Lines 10 to 18Each enum value maps to a message; the compiler warns if a value isn't handled.
- Line 19Printing an enum shows its name, and casting to int shows its number.
- Line 22Values are numbered from 0 in the order they're declared.
Parsing input and listing values
Console.WriteLine("Choose a size: " + string.Join(", ", Enum.GetValues<Size>()));
string[] inputs = { "medium", "XL", "Large" };
foreach (string input in inputs)
{
if (Enum.TryParse<Size>(input, ignoreCase: true, out Size size) && Enum.IsDefined(size))
{
Console.WriteLine($"{input} -> {size}, price {Price(size):F2}");
}
else
{
Console.WriteLine($"{input} is not a valid size");
}
}
decimal Price(Size size) => size switch
{
Size.Small => 2.50m,
Size.Medium => 3.00m,
Size.Large => 3.50m,
_ => throw new ArgumentOutOfRangeException(nameof(size))
};
enum Size { Small = 1, Medium = 2, Large = 3 }Choose a size: Small, Medium, Large
medium -> Medium, price 3.00
XL is not a valid size
Large -> Large, price 3.50Line-by-line explanation
- Line 1GetValues lists every value, which suits menus and dropdowns.
- Line 6TryParse with ignoreCase accepts "medium"; IsDefined rejects numbers that aren't real sizes.
- Lines 16 to 22A local function maps each size to a price.
- Line 21An unexpected value throws instead of returning a wrong price.
Combining permissions with flags
Permission viewer = Permission.Read;
Permission editor = Permission.Read | Permission.Write;
Permission admin = editor | Permission.Delete;
Console.WriteLine(editor);
Console.WriteLine(admin);
Console.WriteLine(editor.HasFlag(Permission.Delete));
Console.WriteLine(admin.HasFlag(Permission.Write | Permission.Delete));
Console.WriteLine((int)admin);
editor &= ~Permission.Write;
Console.WriteLine(editor == viewer);
[Flags]
enum Permission
{
None = 0,
Read = 1,
Write = 2,
Delete = 4
}Read, Write
Read, Write, Delete
False
True
7
TrueLine-by-line explanation
- Line 2| combines two flags into one value.
- Line 5ToString on a flags enum lists every option that's set.
- Line 8HasFlag with several flags is true only if all of them are set.
- Line 91 + 2 + 4 = 7, because each flag is its own bit.
- Line 11&= ~ removes a flag.
- Line 14[Flags] tells .NET to treat the values as combinable options.
Common C# Types Mistakes
Magic numbers instead of an enum
if (order.Status == 2) { } // what does 2 mean?if (order.Status == OrderStatus.Shipped) { }Enum.Parse on user input
var size = Enum.Parse<Size>(input); // ArgumentException on "XL"if (!Enum.TryParse<Size>(input, true, out var size))
{
size = Size.Medium;
}Flags values that overlap
[Flags] enum Options { A = 1, B = 2, C = 3 } // C is the same as A | B[Flags] enum Options { None = 0, A = 1, B = 2, C = 4 }C# Types in Real Projects
Enums in .NET applications
HttpStatusCode, DayOfWeek, ConsoleColor and FileMode are all enums from .NET itself. Business applications use enums for order states, user roles and payment methods, and ASP.NET Core can serialise them to JSON as strings with JsonStringEnumConverter. File system attributes and permission systems use [Flags] enums so one value can describe several settings.
C# Types FAQ
How do I convert a string to an enum in C#?
Use Enum.TryParse<OrderStatus>(text, ignoreCase: true, out var status), which returns false instead of throwing when the text doesn't match a name. Enum.Parse<OrderStatus>(text) works when you're sure the text is valid. Be aware that both also accept numeric strings such as "2", so check with Enum.IsDefined if that matters.
How do I convert an enum to an int in C#?
Cast it: int value = (int)OrderStatus.Paid;. Going the other way also uses a cast, (OrderStatus)1, but it accepts any number, even one with no matching name. Check the result with Enum.IsDefined when the number comes from outside your program.
How do I loop through all values of an enum?
Call Enum.GetValues<OrderStatus>() and loop over the result with foreach. If you only need the names as strings, use Enum.GetNames<OrderStatus>(). These are handy for building menus and dropdown lists.
What is the default value of an enum in C#?
The default is whatever member has the value 0, because an enum is a number underneath. If no member is 0, default(MyEnum) is an unnamed value. That's why it's good practice to make the first member, or a None member, equal to 0.
Why do Flags enum values need to be powers of two?
Each power of two (1, 2, 4, 8 and so on) sets exactly one different bit, so combining values with | never confuses one option with another. If you used 1, 2 and 3, the value 3 would mean both "Read and Write" and the third option. Writing values as 1 << 0, 1 << 1, 1 << 2 makes this pattern obvious.
C# Types Exercises
Answer each one and check it instantly. If you get stuck, open the hint before the answer.
Complete the declaration of the enum.
Direction { North, East, South, West }
What does this print?
Console.WriteLine((int)Color.Blue);
enum Color { Red, Green, Blue }
Which method converts user text to an enum without throwing?
What does this print?
var p = Perm.Read | Perm.Write;
Console.WriteLine(p.HasFlag(Perm.Write));
[Flags] enum Perm { None = 0, Read = 1, Write = 2 }
Why should flags enum values be powers of two?
C# Types Quiz
5 questions to check what you learned.
What number does the first enum value get by default?
- 1
- -1
- 0
- It has no number
What does
OrderStatus.Paid.ToString()return?- 1
- "Paid"
- "OrderStatus.Paid"
- null
How do you get every value of an enum?
- MyEnum.All
- MyEnum.Values()
- Enum.GetValues<MyEnum>()
- typeof(MyEnum).Count
Which operator combines flags?
- |
- &
- +
- ^
Why is reordering enum values risky when they're saved as numbers?
- It isn't
- The compiler forbids it
- Enums can't be saved
- Saved numbers would then map to different names, silently changing their meaning