C# Enums

Enums (short for Enumerations) are a powerful feature in C# that allow developers to define a set of named integral constants. Enums enhance code readability, maintainability, and type safety by providing meaningful names for sets of related values. This comprehensive guide delves into everything you need to know about Enums in C#, including declaration, usage, best practices, common mistakes, and advanced topics.

Table of Contents

  1. Introduction to Enums
  2. - What is an Enum?
    - Benefits of Using Enums
  3. Declaring Enums
  4. - Basic Declaration
    - Specifying Underlying Types
    - Explicit Value Assignment
    - Default Values
  5. Using Enums
  6. - Assigning Enum Values
    - Casting Enums
    - Parsing Enums from Strings
    - Enum Methods and Operations
  7. Flags Enums
  8. - What are Flags Enums?
    - Declaring Flags Enums
    - Using Flags Enums
    - Common Use Cases
  9. Advanced Topics
  10. - Enum Methods
    - Enum Constraints in Generics
    - Enum Serialization and Deserialization
    - Working with Bitwise Operations
  11. Best Practices for Enums
  12. Common Mistakes with Enums
  13. Real-World Example
  14. Summary

1. Introduction to Enums

What is an Enum?

An enum is a distinct type that consists of a set of named constants called the enumerator list. Enums are used to represent a collection of related values in a type-safe manner.

Syntax:
enum EnumName
{
    Member1,
    Member2,
    Member3,
    // ...
}

Benefits of Using Enums

- Readability: Use meaningful names instead of numeric values.
- Maintainability: Easier to manage and update related constants.
- Type Safety: Prevents invalid values from being assigned.
- IntelliSense Support: Enhanced developer experience with IDE support.

2. Declaring Enums

2.1 Basic Declaration

Example: Days of the Week
using System;

enum DayOfWeek
{
    Sunday,    // 0
    Monday,    // 1
    Tuesday,   // 2
    Wednesday, // 3
    Thursday,  // 4
    Friday,    // 5
    Saturday   // 6
}

class Program
{
    static void Main()
    {
        DayOfWeek today = DayOfWeek.Friday;
        Console.WriteLine($"Today is {today}"); // Output: Today is Friday
        Console.WriteLine($"Numeric value: {(int)today}"); // Output: Numeric value: 5
    }
}

Sample Output:
Today is Friday
Numeric value: 5

Explanation:
- Enum Declaration: `DayOfWeek` enum with days as members.
- Default Values: Members are assigned integer values starting from `0`.
- Casting: Enums can be cast to their underlying integer values.

2.2 Specifying Underlying Types

By default, the underlying type of enum members is `int`. However, you can specify a different integral type.

Supported Underlying Types:
- `byte`, `sbyte`
- `short`, `ushort`
- `int`, `uint`
- `long`, `ulong`

Example: Enum with Byte Underlying Type
using System;

enum ErrorCode : byte
{
    None = 0,
    NotFound = 1,
    Invalid = 2,
    Timeout = 3,
    Unknown = 255
}

class Program
{
    static void Main()
    {
        ErrorCode code = ErrorCode.Timeout;
        Console.WriteLine($"Error Code: {code}"); // Output: Error Code: Timeout
        Console.WriteLine($"Numeric value: {(byte)code}"); // Output: Numeric value: 3
    }
}

Sample Output:
Error Code: Timeout
Numeric value: 3

Explanation:
- Underlying Type Specification: `ErrorCode` uses `byte` as its underlying type.
- Value Assignment: Explicit values assigned to enum members.

2.3 Explicit Value Assignment

You can assign specific values to enum members, which can be non-sequential or duplicate if needed.

Example: HTTP Status Codes
using System;

enum HttpStatusCode
{
    OK = 200,
    Created = 201,
    Accepted = 202,
    NoContent = 204,
    BadRequest = 400,
    Unauthorized = 401,
    NotFound = 404,
    InternalServerError = 500
}

class Program
{
    static void Main()
    {
        HttpStatusCode status = HttpStatusCode.NotFound;
        Console.WriteLine($"Status: {status}"); // Output: Status: NotFound
        Console.WriteLine($"Numeric value: {(int)status}"); // Output: Numeric value: 404
    }
}

Sample Output:
Status: NotFound
Numeric value: 404

Explanation:
- Explicit Values: Assigns specific numeric values to enum members, useful for mapping to external systems or standards.

2.4 Default Values

If no values are explicitly assigned, enum members are assigned incrementing integer values starting from `0`.

Example: Default Enum Values
using System;

enum Color
{
    Red,    // 0
    Green,  // 1
    Blue    // 2
}

class Program
{
    static void Main()
    {
        Color favorite = Color.Green;
        Console.WriteLine($"Favorite Color: {favorite}"); // Output: Favorite Color: Green
        Console.WriteLine($"Numeric value: {(int)favorite}"); // Output: Numeric value: 1
    }
}

Sample Output:
Favorite Color: Green
Numeric value: 1

Explanation:
- Auto-Incrementing Values: Enum members are automatically assigned incremental integer values starting at `0` if not explicitly specified.

3. Using Enums

Enums can be used in various ways to enhance code clarity and enforce type safety.

3.1 Assigning Enum Values

Enums can be assigned directly using their named constants.

Example: Assigning and Comparing Enums
using System;

enum TrafficLight
{
    Red,
    Yellow,
    Green
}

class Program
{
    static void Main()
    {
        TrafficLight currentLight = TrafficLight.Red;
        Console.WriteLine($"Current Light: {currentLight}"); // Output: Current Light: Red

        // Comparing Enums
        if(currentLight == TrafficLight.Red)
        {
            Console.WriteLine("Stop!");
        }
    }
}

Sample Output:
Current Light: Red
Stop!

Explanation:
- Enum Assignment: `currentLight` is assigned `TrafficLight.Red`.
- Enum Comparison: Enums can be compared using equality operators for control flow.

3.2 Casting Enums

Enums can be cast to and from their underlying integral types.

Example: Casting Enums
using System;

enum Status
{
    Inactive = 0,
    Active = 1,
    Suspended = 2
}

class Program
{
    static void Main()
    {
        // Casting enum to int
        Status userStatus = Status.Active;
        int statusValue = (int)userStatus;
        Console.WriteLine($"Status Value: {statusValue}"); // Output: Status Value: 1

        // Casting int to enum
        int input = 2;
        Status inputStatus = (Status)input;
        Console.WriteLine($"Input Status: {inputStatus}"); // Output: Input Status: Suspended
    }
}

Sample Output:
Status Value: 1
Input Status: Suspended

Explanation:
- Enum to Integer: Casting `Status.Active` to `int` yields `1`.
- Integer to Enum: Casting `2` to `Status` results in `Status.Suspended`.

3.3 Parsing Enums from Strings

Enums can be parsed from strings using methods like `Enum.Parse` and `Enum.TryParse`, enhancing flexibility in scenarios like user input or data deserialization.

Example: Parsing Enums
using System;

enum Color
{
    Red,
    Green,
    Blue,
    Yellow
}

class Program
{
    static void Main()
    {
        string input = "Green";
        if(Enum.TryParse<Color>(input, out Color color))
        {
            Console.WriteLine($"Parsed Color: {color}"); // Output: Parsed Color: Green
        }
        else
        {
            Console.WriteLine("Invalid color.");
        }

        // Parsing with ignore case
        string inputCase = "blue";
        if(Enum.TryParse<Color>(inputCase, true, out Color colorCase))
        {
            Console.WriteLine($"Parsed Color (case-insensitive): {colorCase}"); // Output: Parsed Color (case-insensitive): Blue
        }
    }
}

Sample Output:
Parsed Color: Green
Parsed Color (case-insensitive): Blue

Explanation:
- Enum.TryParse: Safely attempts to parse a string to an enum value without throwing exceptions.
- Case-Insensitive Parsing: The second parameter `true` in `TryParse` allows case-insensitive parsing.

3.4 Enum Methods and Operations

Enums support various built-in methods and operations that facilitate their manipulation and interrogation.

Example: Enum Methods
using System;

enum Season
{
    Spring,
    Summer,
    Autumn,
    Winter
}

class Program
{
    static void Main()
    {
        // Get all enum values
        Array seasons = Enum.GetValues(typeof(Season));
        Console.WriteLine("Available Seasons:");
        foreach(Season s in seasons)
        {
            Console.WriteLine(s);
        }

        // Check if a value is defined in the enum
        bool isDefined = Enum.IsDefined(typeof(Season), "Summer");
        Console.WriteLine($"Is 'Summer' defined? {isDefined}"); // Output: Is 'Summer' defined? True

        // Get name from value
        string seasonName = Enum.GetName(typeof(Season), 2);
        Console.WriteLine($"Season with value 2: {seasonName}"); // Output: Season with value 2: Autumn
    }
}

Sample Output:
Available Seasons:
Spring
Summer
Autumn
Winter
Is 'Summer' defined? True
Season with value 2: Autumn

Explanation:
- Enum.GetValues: Retrieves all values of the enum.
- Enum.IsDefined: Checks if a specific value or name exists in the enum.
- Enum.GetName: Retrieves the name of the enum member corresponding to a specific value.

4. Flags Enums

Flags Enums are used to represent a combination of options or settings using bitwise operations. They allow for more flexible and expressive code when multiple values can be combined.

4.1 What are Flags Enums?

A Flags Enum is an enumeration where each member represents a bit field, allowing multiple enum values to be combined using bitwise operations like OR (`|`) and AND (`&`).

Key Points:
- [Flags] Attribute: Indicates that the enum can be treated as a bit field.
- Power of Two Values: Each enum member should be assigned values that are powers of two to ensure unique bit flags.
- Combination of Flags: Allows representing multiple options simultaneously.

4.2 Declaring Flags Enums

Syntax:
[Flags]
enum EnumName
{
    None = 0,
    Option1 = 1 << 0, // 1
    Option2 = 1 << 1, // 2
    Option3 = 1 << 2, // 4
    // ...
    All = Option1 | Option2 | Option3
}
Example: File Access Permissions
using System;

[Flags]
enum FileAccessPermissions
{
    None = 0,
    Read = 1 << 0,    // 1
    Write = 1 << 1,   // 2
    Execute = 1 << 2, // 4
    ReadWrite = Read | Write, // 3
    All = Read | Write | Execute // 7
}

class Program
{
    static void Main()
    {
        FileAccessPermissions permissions = FileAccessPermissions.Read | FileAccessPermissions.Execute;
        Console.WriteLine($"Permissions: {permissions}"); // Output: Permissions: Read, Execute
        Console.WriteLine($"Has Write Permission: {permissions.HasFlag(FileAccessPermissions.Write)}"); // Output: Has Write Permission: False
    }
}

Sample Output:
Permissions: Read, Execute
Has Write Permission: False

Explanation:
- [Flags] Attribute: Indicates that the enum can be treated as a bit field for bitwise operations.
- Combination: `Read | Execute` combines the `Read` and `Execute` permissions.
- HasFlag Method: Checks if a specific flag is set.

4.3 Using Flags Enums

Flags enums allow combining multiple enum members to represent complex states or configurations.

Example: Combining Flags
using System;

[Flags]
enum Days
{
    None = 0,
    Monday = 1 << 0,    // 1
    Tuesday = 1 << 1,   // 2
    Wednesday = 1 << 2, // 4
    Thursday = 1 << 3,  // 8
    Friday = 1 << 4,    // 16
    Saturday = 1 << 5,  // 32
    Sunday = 1 << 6,    // 64
    Weekend = Saturday | Sunday // 96
}

class Program
{
    static void Main()
    {
        Days meetingDays = Days.Monday | Days.Wednesday | Days.Friday;
        Console.WriteLine($"Meeting Days: {meetingDays}"); // Output: Meeting Days: Monday, Wednesday, Friday

        // Adding a day
        meetingDays |= Days.Tuesday;
        Console.WriteLine($"Updated Meeting Days: {meetingDays}"); // Output: Updated Meeting Days: Monday, Tuesday, Wednesday, Friday

        // Removing a day
        meetingDays &= ~Days.Wednesday;
        Console.WriteLine($"Final Meeting Days: {meetingDays}"); // Output: Final Meeting Days: Monday, Tuesday, Friday
    }
}

Sample Output:
Meeting Days: Monday, Wednesday, Friday
Updated Meeting Days: Monday, Tuesday, Wednesday, Friday
Final Meeting Days: Monday, Tuesday, Friday

Explanation:
- Combining Flags: Use bitwise OR (`|`) to add flags.
- Removing Flags: Use bitwise AND with the complement (`& ~`) to remove flags.

4.4 Common Use Cases for Flags Enums

- Permissions and Access Control: Representing combinations of user permissions.
- State Representation: Managing multiple states or statuses simultaneously.
- Configuration Settings: Combining various configuration options.

Example Use Case: Network Packet Flags
using System;

[Flags]
enum PacketFlags
{
    None = 0,
    SYN = 1 << 0,    // 1
    ACK = 1 << 1,    // 2
    FIN = 1 << 2,    // 4
    RST = 1 << 3,    // 8
    PSH = 1 << 4,    // 16
    URG = 1 << 5     // 32
}

class Program
{
    static void Main()
    {
        PacketFlags flags = PacketFlags.SYN | PacketFlags.ACK;
        Console.WriteLine($"Packet Flags: {flags}"); // Output: Packet Flags: SYN, ACK

        // Checking if FIN flag is set
        bool hasFIN = flags.HasFlag(PacketFlags.FIN);
        Console.WriteLine($"Has FIN flag: {hasFIN}"); // Output: Has FIN flag: False

        // Adding FIN flag
        flags |= PacketFlags.FIN;
        Console.WriteLine($"Updated Packet Flags: {flags}"); // Output: Updated Packet Flags: SYN, ACK, FIN
    }
}

Sample Output:
Packet Flags: SYN, ACK
Has FIN flag: False
Updated Packet Flags: SYN, ACK, FIN

Explanation:
- Initial Flags: Combines `SYN` and `ACK` flags.
- Flag Checks: Determines if a specific flag (`FIN`) is set.
- Flag Modification: Adds `FIN` flag using bitwise OR.

5. Advanced Topics

5.1 Enum Methods

Enums in C# come with several built-in methods that facilitate their manipulation and interrogation.

Example: Using Enum Methods
using System;

enum Status
{
    Inactive = 0,
    Active = 1,
    Suspended = 2,
    Deleted = 3
}

class Program
{
    static void Main()
    {
        // Get all enum values
        Array statuses = Enum.GetValues(typeof(Status));
        Console.WriteLine("All Statuses:");
        foreach(Status s in statuses)
        {
            Console.WriteLine(s);
        }

        // Check if a value is defined
        bool isDefined = Enum.IsDefined(typeof(Status), "Active");
        Console.WriteLine($"Is 'Active' defined? {isDefined}"); // Output: True

        // Get name from value
        string statusName = Enum.GetName(typeof(Status), 2);
        Console.WriteLine($"Status with value 2: {statusName}"); // Output: Suspended

        // Parsing string to enum
        string input = "Deleted";
        if(Enum.TryParse<Status>(input, out Status parsedStatus))
        {
            Console.WriteLine($"Parsed Status: {parsedStatus}"); // Output: Parsed Status: Deleted
        }
    }
}

Sample Output:
All Statuses:
Inactive
Active
Suspended
Deleted
Is 'Active' defined? True
Status with value 2: Suspended
Parsed Status: Deleted

Explanation:
- Enum.GetValues: Retrieves all values of the `Status` enum.
- Enum.IsDefined: Checks if a specific name or value exists in the enum.
- Enum.GetName: Retrieves the name associated with a specific value.
- Enum.TryParse: Attempts to parse a string into an enum value safely.

5.2 Enum Constraints in Generics

C# allows adding constraints to generic types to ensure they are enums. Starting from C# 7.3, you can use the `where T : Enum` constraint.

Example: Generic Method with Enum Constraint
using System;

class Program
{
    static void Main()
    {
        PrintEnumValue(Status.Active);
        PrintEnumValue(DayOfWeek.Monday);
    }

    static void PrintEnumValue<T>(T enumValue) where T : Enum
    {
        Console.WriteLine($"{enumValue} = {(int)(object)enumValue}");
    }
}

enum Status
{
    Inactive = 0,
    Active = 1,
    Suspended = 2,
    Deleted = 3
}

enum DayOfWeek
{
    Sunday,    // 0
    Monday,    // 1
    Tuesday,   // 2
    Wednesday, // 3
    Thursday,  // 4
    Friday,    // 5
    Saturday   // 6
}

Sample Output:
Active = 1
Monday = 1

Explanation:
- Generic Constraint: The method `PrintEnumValue` ensures that only enum types can be passed as arguments.
- Casting to int: `(int)(object)enumValue` is used to convert the enum to its underlying integer value within a generic context.

5.3 Enum Serialization and Deserialization

Enums can be serialized and deserialized in various formats (JSON, XML, binary), which is essential for data storage, transmission, and interoperability.

Example: JSON Serialization with Enums
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

enum Color
{
    Red,
    Green,
    Blue,
    Yellow
}

class Car
{
    public string Make { get; set; }
    
    [JsonConverter(typeof(StringEnumConverter))]
    public Color Color { get; set; }
}

class Program
{
    static void Main()
    {
        Car car = new Car { Make = "Toyota", Color = Color.Green };
        string json = JsonConvert.SerializeObject(car, Formatting.Indented);
        Console.WriteLine("Serialized JSON:");
        Console.WriteLine(json);
        
        // Deserialize
        Car deserializedCar = JsonConvert.DeserializeObject<Car>(json);
        Console.WriteLine($"Deserialized Car: Make = {deserializedCar.Make}, Color = {deserializedCar.Color}");
    }
}

Sample Output:
Serialized JSON:
{ "Make": "Toyota", "Color": "Green" }
Deserialized Car: Make = Toyota, Color = Green

Explanation:
- StringEnumConverter: Ensures that enums are serialized and deserialized as their string names instead of numeric values.
- Serialization: Converts the `Car` object with the `Color` enum to a JSON string.
- Deserialization: Reconstructs the `Car` object from the JSON string, correctly interpreting the enum.

5.4 Working with Bitwise Operations

Flags enums facilitate bitwise operations, allowing developers to manipulate and inspect combined enum values efficiently.

Example: Bitwise Operations with Flags Enum
using System;

[Flags]
enum Permissions
{
    None = 0,
    Read = 1 << 0,    // 1
    Write = 1 << 1,   // 2
    Execute = 1 << 2, // 4
    Delete = 1 << 3,  // 8
    FullControl = Read | Write | Execute | Delete // 15
}

class Program
{
    static void Main()
    {
        Permissions userPermissions = Permissions.Read | Permissions.Write;
        Console.WriteLine($"User Permissions: {userPermissions}"); // Output: Read, Write

        // Add Execute permission
        userPermissions |= Permissions.Execute;
        Console.WriteLine($"Updated Permissions: {userPermissions}"); // Output: Read, Write, Execute

        // Remove Write permission
        userPermissions &= ~Permissions.Write;
        Console.WriteLine($"Final Permissions: {userPermissions}"); // Output: Read, Execute

        // Check if user has Delete permission
        bool hasDelete = (userPermissions & Permissions.Delete) == Permissions.Delete;
        Console.WriteLine($"Has Delete Permission: {hasDelete}"); // Output: Has Delete Permission: False

        // Assign FullControl
        userPermissions = Permissions.FullControl;
        Console.WriteLine($"User Permissions: {userPermissions}"); // Output: FullControl
    }
}

Sample Output:
User Permissions: Read, Write
Updated Permissions: Read, Write, Execute
Final Permissions: Read, Execute
Has Delete Permission: False
User Permissions: FullControl

Explanation: - Combining Flags: Uses bitwise OR (`|`) to add permissions.
- Removing Flags: Uses bitwise AND with the complement (`& ~`) to remove permissions.
- Checking Flags: Uses bitwise AND (`&`) to verify if a specific permission is set.
- FullControl: Represents all permissions combined.

6. Best Practices for Enums

Adhering to best practices ensures that enums are used effectively, enhancing code quality and maintainability.

6.1 Use the [Flags] Attribute Appropriately

- Apply [Flags] Only When Needed: Use the `[Flags]` attribute for enums intended to be used as bit fields.
- Avoid [Flags] for Single Options: Do not apply `[Flags]` to enums representing mutually exclusive states.

6.2 Provide Explicit Values

- Assign Values Carefully: Ensure that enum members have explicit values, especially when the underlying type is not `int`.
- Maintain Consistency: Use consistent value assignments to prevent confusion and errors.

6.3 Use Singular Naming

- Enum Names: Use singular nouns for enum names (e.g., `DayOfWeek`, `Color`).
- Member Names: Use clear and descriptive names for enum members.

6.4 Avoid Duplicate Values Unless Intentional

- Unique Values: Assign unique values to enum members unless multiple members are meant to represent the same value.
- Clear Intent: Document or comment on why duplicate values exist to maintain clarity.

6.5 Implement Interfaces Carefully

- Prefer Generic Interfaces: When implementing interfaces, prefer generic versions like `IComparable` to avoid boxing.
- Limit Interface Implementations: Only implement necessary interfaces to keep enums simple and efficient.

6.6 Default Enum Member

- Meaningful Default: Ensure that the default enum member (`0`) represents a valid and meaningful state, such as `None` or `Unknown`.
- Avoid Undefined Defaults: Prevent scenarios where the default value does not correspond to any defined enum member.

Example: Meaningful Default
using System;

enum Status
{
    Unknown = 0,
    Active = 1,
    Inactive = 2,
    Suspended = 3
}

class Program
{
    static void Main()
    {
        Status defaultStatus = default(Status);
        Console.WriteLine($"Default Status: {defaultStatus}"); // Output: Default Status: Unknown
    }
}

Sample Output:
Default Status: Unknown

7. Common Mistakes with Enums

Avoiding common pitfalls ensures that enums are used correctly and efficiently.

7.1 Duplicate Values

Mistake: Assigning the same value to multiple enum members unintentionally. Example:
enum Status
{
    Active = 1,
    Inactive = 1, // Duplicate value
    Suspended = 2
}
Issue: `Active` and `Inactive` both have the value `1`, which can lead to ambiguity.

Solution: Ensure that each enum member has a unique value unless intentionally representing the same state.

7.2 Invalid Casting

Mistake: Casting integers that are not defined in the enum can lead to undefined or unexpected states. Example:
using System;

enum Color
{
    Red = 1,
    Green = 2,
    Blue = 3
}

class Program
{
    static void Main()
    {
        int input = 4;
        Color color = (Color)input;
        Console.WriteLine($"Color: {color}"); // Output: Color: 4 (undefined)
    }
}
Issue: The value `4` does not correspond to any defined member, leading to an undefined enum state.

Solution:
- Use Enum.IsDefined: Check if a value is defined before casting.
- Use Enum.TryParse: Safely parse strings to enum values.

Corrected Example:
using System;

enum Color
{
    Red = 1,
    Green = 2,
    Blue = 3
}

class Program
{
    static void Main()
    {
        int input = 4;
        if(Enum.IsDefined(typeof(Color), input))
        {
            Color color = (Color)input;
            Console.WriteLine($"Color: {color}");
        }
        else
        {
            Console.WriteLine("Invalid color value.");
        }
    }
}

Sample Output:
Invalid color value.

7.3 Missing [Flags] Attribute for Bitwise Enums

Mistake: Forgetting to add the `[Flags]` attribute when designing enums for bitwise operations. Example:
enum Permissions
{
    None = 0,
    Read = 1,
    Write = 2,
    Execute = 4
}
Issue: Without `[Flags]`, combining enum members might not produce the expected string representation.

Solution: Add the `[Flags]` attribute to indicate that the enum can be treated as a bit field.

Corrected Example:
using System;

[Flags]
enum Permissions
{
    None = 0,
    Read = 1,
    Write = 2,
    Execute = 4
}

class Program
{
    static void Main()
    {
        Permissions userPermissions = Permissions.Read | Permissions.Execute;
        Console.WriteLine($"User Permissions: {userPermissions}"); // Output: User Permissions: Read, Execute
    }
}

7.4 Using Enums for Unrelated Constants

Mistake: Applying enums to represent unrelated or heterogeneous values, reducing code clarity.

Example:
enum Settings
{
    MaxUsers = 100,
    Timeout = 30,
    Theme = 1
}
Issue: `MaxUsers` and `Theme` represent different concepts (numeric limit vs. configuration option), leading to confusion.

Solution: Use separate enums or appropriate data structures for unrelated constants.

7.5 Overusing Enums

Mistake: Using enums in situations where other data structures (like classes or structs) would be more appropriate, leading to rigid and less flexible code.

Example:
enum UserType
{
    Admin,
    Guest,
    RegisteredUser,
    // Later adding more types becomes cumbersome
}
Issue: As requirements grow, adding more user types to the enum can lead to bloated and less manageable code.

Solution: Use classes with inheritance or other design patterns for more complex scenarios.

8. Real-World Example

Example: Traffic Light Control System

Enums are ideal for representing states in a state machine, such as a traffic light system. This example demonstrates using enums to manage traffic light states and transitions.

Code Example: Traffic Light Controller
using System;
using System.Threading;

enum TrafficLightState
{
    Red,
    Green,
    Yellow
}

class TrafficLightController
{
    private TrafficLightState _currentState;

    public TrafficLightController()
    {
        _currentState = TrafficLightState.Red;
    }

    public void Start()
    {
        while(true)
        {
            DisplayState();
            switch(_currentState)
            {
                case TrafficLightState.Red:
                    Thread.Sleep(3000); // Red for 3 seconds
                    _currentState = TrafficLightState.Green;
                    break;
                case TrafficLightState.Green:
                    Thread.Sleep(3000); // Green for 3 seconds
                    _currentState = TrafficLightState.Yellow;
                    break;
                case TrafficLightState.Yellow:
                    Thread.Sleep(1000); // Yellow for 1 second
                    _currentState = TrafficLightState.Red;
                    break;
            }
        }
    }

    private void DisplayState()
    {
        Console.Clear();
        Console.WriteLine("Traffic Light State:");
        switch(_currentState)
        {
            case TrafficLightState.Red:
                Console.ForegroundColor = ConsoleColor.Red;
                Console.WriteLine("RED");
                break;
            case TrafficLightState.Green:
                Console.ForegroundColor = ConsoleColor.Green;
                Console.WriteLine("GREEN");
                break;
            case TrafficLightState.Yellow:
                Console.ForegroundColor = ConsoleColor.Yellow;
                Console.WriteLine("YELLOW");
                break;
        }
        Console.ResetColor();
    }
}

class Program
{
    static void Main()
    {
        TrafficLightController controller = new TrafficLightController();
        controller.Start();
    }
}
Explanation:
- Enum Declaration: `TrafficLightState` defines possible states of a traffic light.
- State Management: The controller transitions between `Red`, `Green`, and `Yellow` states with specified durations.
- Display: The current state is displayed with corresponding console colors.
- Continuous Loop: The `Start` method runs an infinite loop to simulate continuous traffic light cycles.

Sample Output: A console window will display the current traffic light state in color, cycling through Red, Green, and Yellow with appropriate delays.

9. Summary

Enums in C# are a robust feature that provide a way to define a set of named integral constants, enhancing code readability, maintainability, and type safety. By leveraging enums effectively, developers can create more expressive and error-resistant code.

Key Takeaways:
- Definition and Declaration: Enums are defined using the `enum` keyword and can have explicit underlying types and values.
- Usage: Enums can be assigned, compared, cast, and parsed, making them versatile for various scenarios.
- Flags Enums: With the `[Flags]` attribute, enums can represent combinations of values, useful for settings and permissions.
- Advanced Features: Enums support methods, generic constraints, serialization, and bitwise operations, providing extensive functionality.
- Best Practices: Use enums judiciously, ensure meaningful naming and values, apply the `[Flags]` attribute appropriately, and avoid common mistakes like duplicate values or invalid casting.
- Real-World Applications: Enums are ideal for state management, configuration settings, error codes, and more, enabling clear and maintainable code structures.

By mastering enums, you can effectively manage sets of related constants, leading to cleaner, more maintainable, and robust C# applications.

Previous: C# Structs | Next: C# Generics

<
>