Enums in .NET can not only express fixed value sets, but also support Flags attributes for bitwise operations and built-in reflection methods for type conversion. From basic enum definitions to Swagger integration in ASP.NET Core APIs, this guide will help you master all capabilities of C# enums.
This guide covers enum-to-string conversion, Flags bit flags, collection operations, JSON serialization, and API documentation generation.
C# Enum Fundamentals & Flags Design
C# enums default to int type, with each member auto-incrementing. You can also specify other integer types (byte, short, long) as the base type. The Flags attribute enables enums to support bitwise operations, suitable for representing multiple combinable options.
Also covers default value conventions, integer base type selection, and daily naming considerations.
Conversion: Strings, Lists & Collections
.NET provides rich enum conversion APIs, allowing you to easily convert between enums, strings, integers, and collections.
String ↔ Enum
Enum.TryParse("Paid", out OrderStatus status);
var status = Enum.Parse("Refunded", ignoreCase: true);
var name = OrderStatus.Paid.ToString();
var value = (int)OrderStatus.Paid;
TryParse is the safest string conversion method, avoiding exceptions that direct Parse might throw.
Enum Collections & Lists
var values = Enum.GetValues();
var list = Enum.GetValues(typeof(OrderStatus))
.Cast()
.ToList();
var names = Enum.GetNames();
GetValues and GetNames are the most commonly used batch operation methods, suitable for dropdown lists, option displays, etc.
Swagger / JSON Serialization
In ASP.NET Core APIs, enums need to be correctly serialized to JSON and clearly displayed in Swagger documentation. By default, enums serialize to numbers, but usually we want to use string representation.
• Use Enum.TryParse instead of direct conversion to avoid exceptions
✓ Best Practices
• Use Enum.GetValues to iterate all enum values
• Create extension methods to unify enum conversion handling
• Unit tests cover business logic for all enum values
• Document enum change history to avoid breaking API contracts
Naming and Design Anti-patterns
❌ Anti-pattern: Directly converting integer to enum
int value = GetUserInput();
OrderStatus status = (OrderStatus)value; // Unsafe!
Issue: If value is outside enum definition range, produces invalid enum
✅ Correct: Use Enum.IsDefined or TryParse
if (Enum.IsDefined(typeof(OrderStatus), value))
{
var status = (OrderStatus)value;
}
// Or use TryParse
if (Enum.TryParse(input, out var status))
{
// Safe to use
}
// 1. Enum definition
using System.ComponentModel;
using System.Text.Json.Serialization;
[JsonConverter(typeof(JsonStringEnumConverter))]
public enum OrderStatus
{
[Description("Pending")]
Pending,
[Description("Processing")]
Processing,
[Description("Completed")]
Completed
}
// 2. Program.cs configuration
builder.Services.AddControllers()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.Converters.Add(
new JsonStringEnumConverter());
});
// 3. Swagger configuration
builder.Services.AddSwaggerGen(c =>
{
c.UseInlineDefinitionsForEnums();
c.SchemaFilter();
});
// 4. Custom Schema Filter
public class EnumSchemaFilter : ISchemaFilter
{
public void Apply(OpenApiSchema schema, SchemaFilterContext context)
{
if (context.Type.IsEnum)
{
schema.Enum.Clear();
foreach (var name in Enum.GetNames(context.Type))
{
schema.Enum.Add(new OpenApiString(name));
}
}
}
}
Tip: JsonStringEnumConverter makes API return strings instead of numbers
xUnit Test Template
Ensure enums are correctly used across layers:
using Xunit;
using FluentAssertions;
public class OrderStatusTests
{
[Theory]
[InlineData("Pending", OrderStatus.Pending)]
[InlineData("Processing", OrderStatus.Processing)]
[InlineData("Completed", OrderStatus.Completed)]
public void TryParse_ShouldParseCorrectly(
string input, OrderStatus expected)
{
Enum.TryParse(input, out var result)
.Should().BeTrue();
result.Should().Be(expected);
}
[Fact]
public void AllEnumValues_ShouldHaveDescription()
{
foreach (OrderStatus status in Enum.GetValues())
{
var description = status.GetDescription();
description.Should().NotBeNullOrEmpty();
}
}
[Theory]
[MemberData(nameof(AllStatusValues))]
public void BusinessLogic_ShouldHandleAllStatuses(
OrderStatus status)
{
var result = OrderService.ProcessStatus(status);
result.Should().NotBeNull();
}
public static IEnumerable
Tip: Use MemberData to automatically test all enum values
Practical Extension Methods Library
using System.ComponentModel;
using System.Reflection;
public static class EnumExtensions
{
public static string GetDescription(this Enum value)
{
var field = value.GetType().GetField(value.ToString());
var attribute = field?.GetCustomAttribute();
return attribute?.Description ?? value.ToString();
}
public static T? ParseOrDefault(this string value, T? defaultValue = default)
where T : struct, Enum
{
return Enum.TryParse(value, ignoreCase: true, out var result)
? result
: defaultValue;
}
public static IEnumerable GetFlags(this T value)
where T : struct, Enum
{
return Enum.GetValues()
.Where(v => value.HasFlag(v) && !v.Equals(default(T)));
}
public static List<(T Value, string Name, string Description)> ToList()
where T : struct, Enum
{
return Enum.GetValues()
.Select(v => (
Value: v,
Name: v.ToString(),
Description: v.GetDescription()
))
.ToList();
}
}
Tip: These extension methods can unify enum handling logic across the project
C# Enum FAQ
How to implement c# enum string value?
Can read `DescriptionAttribute` through extension methods or build a custom dictionary; use JSON Converter for external string output.
Does enum.TryParse have case sensitivity issues?
Use `Enum.TryParse("value", ignoreCase: true, out var result)`, and add defensive checks for null values.
How to document Flags enums?
Explain combination methods in Swagger, provide examples like `{ "permissions": ["Read", "Write"] }`, backend uses array to bitmask conversion.
How to expose enums to frontend?
Provide `Enum.GetNames()` results to frontend, combined with TypeScript `as const` or `enum` definitions; keep documentation synchronized.