Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

C# & .NET (C# 14, .NET 10 LTS)  Module 2 – Types, variables and operators

Enums and flag enums

Define C# enums with explicit values, combine options in a [Flags] enum, parse input safely and handle the values no member names, such as (TrainClass)7.

  • Beginner
  • 15 minutes
  • Examples run with .NET 10.0.401 and Python 3.14.8
  • By MySmartCoPilot
Free core, Pro depth: 5 of 15 minutes free
Free: An enum names numbers, and every number of its type is a value · A flags enum gives every option its own bit · Enum.TryParse reads names, and numbers too. With a pass: 3 in-depth parts and 3 interview answers, about 10 minutes.

What you will learn

  • Define an enum with explicit underlying values, and convert between it and its number
  • Combine, test and print the options of a [Flags] enum
  • Validate parsed or cast enum values with Enum.TryParse and Enum.IsDefined
  • Handle the values that no member names in a switch expression over an enum
  • Choose how to store an enum, as a number or as a name, so that a new version reads it right

Before you start

What you’ll learn · Before you start

What you will learn

  • Define an enum with explicit underlying values, and convert between it and its number
  • Combine, test and print the options of a [Flags] enum
  • Validate parsed or cast enum values with Enum.TryParse and Enum.IsDefined
  • Handle the values that no member names in a switch expression over an enum
  • Choose how to store an enum, as a number or as a name, so that a new version reads it right

Before you start

On this page

(TrainClass)7 compiles and runs in C#, although TrainClass names only four classes of ticket, numbered 1 to 4: the program prints 7 and carries on. enum: A value type that gives names to constants of an integer type, int unless the declaration names another. Any number of that type converts to the enum, whether a member names it or not. give names to numbers, and every number of the enum’s integer type is a value of it, named or not. A flags enum: An enum marked [Flags] whose members are powers of two, so each owns one bit and | combines several in one value. ToString names every set bit, such as Window, Lower. gives each option one bit, so one value can hold several options at once.

You will define enums with explicit numbers, combine options in flag enums, read an enum from text without letting a 7 through, and handle the values that no member names.

An enum names numbers, and every number of its type is a value

An enum declares a type whose values are named constants of an integer type, int unless you choose another. That integer type is the enum’s underlying type: The integer type that an enum stores its values in: int unless the declaration names another, as in enum Berth : byte. It decides the size of a value and the numbers it can hold.: it sets the size of each value and the numbers it can hold. Writing each number yourself, as a database or an API stores it, keeps the numbers fixed when members are added later:

basics.cs: a TrainClass enum, cast both ways C# · basics/basics.cs
using System.Globalization;

CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;

// The class of a train ticket, with the number a booking system stores for each.
TrainClass chosen = TrainClass.AC3;
Console.WriteLine($"{chosen} is stored as {(int)chosen}");
Console.WriteLine($"(TrainClass)4 is {(TrainClass)4}");

// Any int converts to the enum, named or not.
TrainClass unknown = (TrainClass)7;
Console.WriteLine($"(TrainClass)7 prints as {unknown}, defined: {Enum.IsDefined(unknown)}");

TrainClass fresh = default;
Console.WriteLine($"default(TrainClass) prints as {fresh}, defined: {Enum.IsDefined(fresh)}");

Console.WriteLine($"members: {string.Join(", ", Enum.GetValues<TrainClass>().Select(c => $"{c} = {(int)c}"))}");
Console.WriteLine($"AC3 < AC2: {TrainClass.AC3 < TrainClass.AC2}, AC3 + 1 = {TrainClass.AC3 + 1}");
Console.WriteLine($"underlying type: {Enum.GetUnderlyingType(typeof(TrainClass)).Name}");

enum TrainClass
{
    Sleeper = 1,
    AC3 = 2,
    AC2 = 3,
    AC1 = 4,
}

Output

AC3 is stored as 2
(TrainClass)4 is AC1
(TrainClass)7 prints as 7, defined: False
default(TrainClass) prints as 0, defined: False
members: Sleeper = 1, AC3 = 2, AC2 = 3, AC1 = 4
AC3 < AC2: True, AC3 + 1 = AC2
underlying type: Int32

Recorded with .NET SDK 10.0.401 (runtime 10.0.12) on macOS 26 arm64. To run it yourself: dotnet run basics.cs

(int)chosen gave 2, the number a booking system would store for AC3, and (TrainClass)4 gave AC1 back: explicit casts convert both ways. (TrainClass)7 was accepted as well and printed as 7, because any int converts to the enum, and only Enum.IsDefined said that no member has the value. The default is 0 although no member has it, and Microsoft’s reference advises almost always defining a member with the value 0, such as None or Unknown, so that a new variable means something. Underneath, enums stay numbers: AC3 < AC2 compared 2 with 3, and AC3 + 1 gave AC2.

Trusting a cast or a stored number

(TrainClass)code never fails, so a code of 7 read from a database becomes a TrainClass that no member names. Check Enum.IsDefined(value) where numbers enter the program, or reject the value in a switch with a throwing arm.

Checkpoint

Check: Enums as named numbers

  1. Question 1 of 2 enum TrainClass { Sleeper = 1, AC3 = 2, AC2 = 3, AC1 = 4 } — what is (int)TrainClass.AC2?

    Type a number.

    Show the answer: Question 1 of 2

    Answer: 3

    A cast to the underlying type gives a member's number, written in the declaration: AC2 = 3.

  2. Question 2 of 2 TrainClass fresh = default; what does Console.WriteLine(fresh) print?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: 0

    Microsoft's reference advises almost always defining a member with the value 0, such as None or Unknown, so that a variable that was never set still means something.

A flags enum gives every option its own bit

Some choices combine: a passenger may want a window and a lower berth. Flag enums give each option a power of two, so each owns one bit and | combines any set of them in one value. The [Flags] attribute tells .NET that values are combinations, and ToString names each option they hold:

flags.cs: seat preferences combined, tested and printed C# · flags/flags.cs
using System.Globalization;

CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;

// Seat preferences: each option owns one bit, so any set of them fits in one value.
SeatPreference wanted = SeatPreference.Window | SeatPreference.Lower;
Console.WriteLine($"Window | Lower = {wanted} ({(int)wanted}, bits {Bits(wanted)})");
Console.WriteLine($"HasFlag(Lower) {wanted.HasFlag(SeatPreference.Lower)}, HasFlag(Aisle) {wanted.HasFlag(SeatPreference.Aisle)}");

wanted |= SeatPreference.Side;
Console.WriteLine($"after |= Side:    {wanted} (bits {Bits(wanted)})");
wanted &= ~SeatPreference.Window;
Console.WriteLine($"after &= ~Window: {wanted} (bits {Bits(wanted)})");

Console.WriteLine($"None = {SeatPreference.None}, Lower | Upper = {SeatPreference.Lower | SeatPreference.Upper}");
Console.WriteLine($"(SeatPreference)13 = {(SeatPreference)13}");
Console.WriteLine($"(SeatPreference)64 = {(SeatPreference)64}, (SeatPreference)69 = {(SeatPreference)69}");

// The same numbers in an enum without [Flags] print as numbers.
Console.WriteLine($"without [Flags], 5 = {(PlainPreference)5}");

// A [Flags] enum of .NET itself: Unix file permissions, 755 in octal.
UnixFileMode mode = (UnixFileMode)Convert.ToInt32("755", 8);
Console.WriteLine($"755 = {mode}");

static string Bits(SeatPreference p) => Convert.ToString((int)p, 2).PadLeft(5, '0');

[Flags]
enum SeatPreference
{
    None = 0,
    Window = 1,
    Aisle = 2,
    Lower = 4,
    Upper = 8,
    Side = 16,
    AnyBerth = Lower | Upper,
}

enum PlainPreference
{
    None = 0,
    Window = 1,
    Aisle = 2,
    Lower = 4,
    Upper = 8,
    Side = 16,
}

Output

Window | Lower = Window, Lower (5, bits 00101)
HasFlag(Lower) True, HasFlag(Aisle) False
after |= Side:    Window, Lower, Side (bits 10101)
after &= ~Window: Lower, Side (bits 10100)
None = None, Lower | Upper = AnyBerth
(SeatPreference)13 = Window, AnyBerth
(SeatPreference)64 = 64, (SeatPreference)69 = 69
without [Flags], 5 = 5
755 = OtherExecute, OtherRead, GroupExecute, GroupRead, UserExecute, UserWrite, UserRead

Recorded with .NET SDK 10.0.401 (runtime 10.0.12) on macOS 26 arm64. To run it yourself: dotnet run flags.cs

Window | Lower printed as Window, Lower, the value 5, bits 00101. HasFlag tested one option, |= added Side and &= ~ removed Window, the same bit operators that the operators lesson used on plain integers. None names zero, and a member can name a combination: AnyBerth is Lower | Upper. Step through how each value became text:

Interactive · stage by stage How a SeatPreference value becomes text Step through the values that flags.cs printed. The arrows aside are the two ways out before the bits are named.
How a SeatPreference value becomes text, step 6 of 6. Showing A SeatPreference value (PlainPreference 5), One member equals it?, Name the bits it holds (not used), Any bit without a name? (not used), The text (5). In focus: One member equals it?, The text.yes: its nameyes: the numberA SeatPreference valuePlainPreference 5One member equals it?tried firstName the bits it holdsnot usedAny bit without a name?not usedThe text5

The drawing shows the whole scene; its 6 steps are listed below.

Every step as text
  1. 5 arrives. flags.cs prints Window | Lower, the value 5, bits 00101. No member has the value 5, so the bits it holds are named one by one.
  2. Two names. Bit 1 is Window and bit 4 is Lower, and no bit is left over, so the text is both names, lowest first: Window, Lower.
  3. A combination member. AnyBerth is declared as Lower | Upper, 12, so the value Lower | Upper equals a member, and its one name is printed: AnyBerth.
  4. 13. 13 is Window, Lower and Upper. The run printed Window, AnyBerth: the combination member took bits 4 and 8, and Window took bit 1.
  5. A bit with no name. 69 is 64 + 4 + 1, and no member has bit 64. With a bit left over that no member names, the whole value prints as a number: 69, and 64 alone as 64.
  6. Without [Flags]. PlainPreference has the same members without [Flags]. There the value 5 printed as 5: without the attribute, only a value that one member equals gets a name.

A bit that no member names makes the whole value print as a number, 64 and 69, and without [Flags] the same 5 printed as 5. .NET uses the same design for its own options: UnixFileMode printed 755 as the names of the seven permission bits it sets.

Chmod Calculator Type 755 and compare its ticked boxes with the seven names that UnixFileMode printed.

Checkpoint

Check: Flags enums and how they print

  1. Question 1 of 2 [Flags] enum SeatPreference { None = 0, Window = 1, Aisle = 2, Lower = 4, Upper = 8, Side = 16 } — what does (SeatPreference)6 print?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: Aisle, Lower

    With [Flags], a value that no single member equals is printed as the names of its bits, separated by commas.

  2. Question 2 of 2 Which statement removes Window from wanted and leaves every other option as it was?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: wanted &= ~SeatPreference.Window;

    Set an option with |, clear it with & and the complement, test it with HasFlag or with & against zero.

Enum.TryParse reads names, and numbers too

Text from a form or a file has to become an enum value. Enum.TryParse reads a member’s name, ignoring case when you ask, and returns false for a name it does not know:

parse.cs: eight texts read as TrainClass C# · parse/parse.cs
using System.Globalization;

CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;

// Eight texts a form might send, read with Enum.TryParse, ignoring case.
string[] inputs = ["AC3", "ac3", " AC2 ", "Sleeper, AC1", "7", "-1", "AC4", ""];
Console.WriteLine($"{"input",-16} {"TryParse",-15} IsDefined");
foreach (string input in inputs)
{
    bool parsed = Enum.TryParse(input, ignoreCase: true, out TrainClass value);
    string defined = parsed ? Enum.IsDefined(value).ToString() : "-";
    Console.WriteLine($"{$"\"{input}\"",-16} {$"{parsed}, {value}",-15} {defined}");
}

bool exactCase = Enum.TryParse("ac3", out TrainClass _);
Console.WriteLine($"without ignoreCase, \"ac3\" gives {exactCase}");

enum TrainClass
{
    Sleeper = 1,
    AC3 = 2,
    AC2 = 3,
    AC1 = 4,
}

Output

input            TryParse        IsDefined
"AC3"            True, AC3       True
"ac3"            True, AC3       True
" AC2 "          True, AC2       True
"Sleeper, AC1"   True, 5         False
"7"              True, 7         False
"-1"             True, -1        False
"AC4"            False, 0        -
""               False, 0        -
without ignoreCase, "ac3" gives False

Recorded with .NET SDK 10.0.401 (runtime 10.0.12) on macOS 26 arm64. To run it yourself: dotnet run parse.cs

ac3 and " AC2 ", with its spaces, were read. Three results need a second look: 7 and -1 were accepted as numbers, and Sleeper, AC1 as a list combined into 5, although TrainClass is not a flags enum. Microsoft’s documentation of TryParse describes both: a list is combined with a bitwise OR, and text that is a number becomes that number even when no member has it. Follow four of the inputs through the two checks:

Interactive · step by step Reading a TrainClass from text Step through four inputs of parse.cs. A check that fails sends the input to the rejection on the right.
Reading a TrainClass from text, step 5 of 5. Showing Text from a form ("AC4"), Enum.TryParse (false: 0), Enum.IsDefined (not reached), Use the value (waiting), Reject the input.truetruefalsefalseText from a form"AC4"Enum.TryParsefalse: 0Enum.IsDefinednot reachedUse the valuewaitingReject the inputwith a message

The drawing shows the whole scene; its 5 steps are listed below.

Every step as text
  1. "ac3". TryParse with ignoreCase reads ac3 as AC3, and IsDefined confirms that a member has its value, 2. The value can be used.
  2. "7". TryParse accepts the digits 7 and returns the value 7, which no member names. Only IsDefined catches it and sends it to the rejection.
  3. "Sleeper, AC1". A comma-separated list is read as Sleeper | AC1, the value 5, although TrainClass is not a flags enum. IsDefined rejects 5 as well.
  4. "AC4". AC4 is a name that no member has, so TryParse itself returns false. Its result is 0, the default, which is no member either.
  5. Two checks. TryParse says whether the text could be read; IsDefined says whether the value is one of the members. Input from outside the program needs both.

Key idea

An enum value is a number that has a name when a member has that value. A cast, a stored number and parsed text can all hold a number without a name, so check each value where it enters the program.

Checkpoint

Check: Reading an enum from text

  1. Question 1 of 2 No TrainClass member has the value 7. What does Enum.TryParse("7", out TrainClass c) return?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: true, and c holds 7

    Microsoft's documentation of TryParse says it: the string of an integer that no member has still converts. Check the result before you trust it.

  2. Question 2 of 2 Enum.TryParse returned true for "7" and for "Sleeper, AC1". Which check then rejects both?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: Enum.IsDefined(c)

    TryParse says whether the text could be read; IsDefined says whether a member has the value. Input from outside needs both checks.

Validating enum input, with every trap of TryParse reproduced Pro: opens with a Pro, Premium or Ultimate pass

Flags need more care than an ordinary enum when text becomes a value. The runs below show what TryParse and IsDefined say about seven texts, then build a parser for typed seat preferences that accepts names only.

In this part:

  • What TryParse and IsDefined say about a flags enum
  • A parser for typed preferences: names only, any case, every problem reported

1 runnable example (C#) · about 4 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Switch expressions over enums: CS8509, CS8524 and the discard arm Pro: opens with a Pro, Premium or Ultimate pass

A switch expression must give a value for every input, and an enum has more inputs than names. The session below builds five versions of one switch over TrainClass and shows which version lets the compiler find a member you forgot.

In this part:

  • Five versions of one switch, built and run
  • Choosing the version for the enum you have

2 runnable examples (C#, Python) · about 5 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Enums over time: stored numbers, JSON and members of their own Pro: opens with a Pro, Premium or Ultimate pass

An enum outlives the code that declares it: its numbers are stored in databases, sent as JSON and read by the next version. The runs below show what a careless new version does to stored numbers, how JSON writes enums, and how C# 14 gives an enum members of its own.

In this part:

  • Numbers that a new version reads differently
  • Members of their own, with C# 14 extension members

2 runnable examples (C#) · about 5 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Practise: add a member and predict the text

Give bit 64 a name

Copy flags.cs to your computer, add Accessible = 64 to SeatPreference and predict what (SeatPreference)69 prints now. Run it with dotnet run flags.cs, then remove [Flags] and predict once more.

Then test yourself on enum values, flags and parsing; the questions follow your answers, and a wrong choice tells you what it missed:

Practice that adapts to you

Practise this lesson

9 questions about 9 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 9 What is the underlying type of an enum declared without one?

    Choose one answer.

    Show the answer: Question 1 of 9

    Answer: int

    Members are constants of an integer type, int unless the declaration says otherwise.

  2. Question 2 of 9 enum TrainClass { Sleeper = 1, AC3 = 2, AC2 = 3, AC1 = 4 } — what does Console.WriteLine((TrainClass)4) print, and what does (TrainClass)9 print?

    Choose one answer.

    Show the answer: Question 2 of 9

    Answer: AC1 and 9

    Every int converts to the enum, so a value without a name is printed as its number, as (TrainClass)7 was.

  3. Question 3 of 9 A booking system stores TrainClass as a number in its database. A new class must be added. Which change keeps every stored booking correct?

    Choose one answer.

    Show the answer: Question 3 of 9

    Answer: Add the member with a new explicit number, such as ChairCar = 5

    Write every number yourself when values are stored or sent, and give a new member a number never used before.

  4. Question 4 of 9 Why are the members of a flags enum powers of two?

    Choose one answer.

    Show the answer: Question 4 of 9

    Answer: So that each owns one bit, and combinations never mix two options into one

    With one bit per option, | combines options and & tests them without one option ever looking like another.

  5. Question 5 of 9 Window = 1, Aisle = 2, Lower = 4, Upper = 8, Side = 16. What number is Window | Upper | Side?

    Type a number.

    Show the answer: Question 5 of 9

    Answer: 25

    1 + 8 + 16 = 25: each option sets its own bit, so OR adds the three values.

  6. Question 6 of 9 A SeatPreference value of a [Flags] enum prints as 69. What does that tell you?

    Choose one answer.

    Show the answer: Question 6 of 9

    Answer: It holds a bit that no member names, here 64

    A flags value prints as names only when every set bit has one. A number in the output is a sign of a bit that came from somewhere unexpected.

  7. Question 7 of 9 Which call reads the text "ac3" as TrainClass.AC3?

    Choose one answer.

    Show the answer: Question 7 of 9

    Answer: Enum.TryParse("ac3", ignoreCase: true, out TrainClass c)

    Enum.TryParse reads names and, with ignoreCase, matches them in any case.

  8. Question 8 of 9 For the ordinary enum TrainClass, Enum.TryParse("Sleeper, AC1", out TrainClass c) returns true. What is c?

    Choose one answer.

    Show the answer: Question 8 of 9

    Answer: 5, a value no member names

    TryParse accepts comma-separated lists for every enum and ORs them, which is one more reason to check the result with IsDefined.

  9. Question 9 of 9 A form sends a train class as text. Which code accepts AC2 in any case and rejects 7, "Sleeper, AC1" and AC4?

    Choose one answer.

    Show the answer: Question 9 of 9

    Answer: TryParse with ignoreCase: true, then reject the value unless Enum.IsDefined(value)

    Read with TryParse, then check that a member has the value. Both checks belong where text enters the program.

Interview questions

Fresher · What is a [Flags] enum, and how is it different from an ordinary enum?

An ordinary enum names values that exclude each other, such as the one class of a ticket. A [Flags] enum names options that combine: each member is a power of two, so each owns one bit, and | stores any set of them in one value, such as Window | Lower, which is 5. The attribute changes how a value prints: ToString names every option it holds, Window, Lower, where an ordinary enum would print 5. Test an option with HasFlag or (value & option) != 0, and define None = 0 for the empty set.

  1. Fresher to mid

    An API takes a TrainClass in its JSON body, and a client sends the number 7, which no member has. What does your service do with it today, where should the value be checked, and how?

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  2. Mid to senior

    A switch expression over an enum has an arm for every member, yet the build warns CS8524. A teammate wants to add _ => throw new InvalidOperationException(). Explain the warning, what the discard changes, and which fix still tells you when someone adds a member.

    Model answer, Pro. First step and follow-ups

  3. Mid to senior

    A release adds ChairCar to the TrainClass enum, right after Sleeper and without explicit numbers. The next morning, existing bookings show the wrong class of travel. Explain what happened, how you would repair the data, and how you would store enums so that this cannot happen again.

    Model answer, Pro. First step and follow-ups

Key takeaways

  • An enum gives names to the numbers of an integer type, and any number of that type is a value of the enum, so (TrainClass)7 compiles and prints 7.
  • Flag enums give every option one bit, | combines options, and [Flags] makes ToString name each one.
  • Enum.TryParse reads names, numbers and comma-separated lists, so parsed input still needs a check that its value has a name.
  • Check enum values where they enter the program, because a cast, a stored number and parsed text never check them.

Terms in this lesson

enum
A value type that gives names to constants of an integer type, int unless the declaration names another. Any number of that type converts to the enum, whether a member names it or not.
flags enum
An enum marked [Flags] whose members are powers of two, so each owns one bit and | combines several in one value. ToString names every set bit, such as Window, Lower.
underlying type
The integer type that an enum stores its values in: int unless the declaration names another, as in enum Berth : byte. It decides the size of a value and the numbers it can hold.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.