MatchGenerator
April 2, 2026 · View on GitHub
Bring exhaustive pattern matching to C# enums and unions with zero boilerplate.
MatchGenerator is a Roslyn source generator that creates Match extension methods for your enums and discriminated-union-like types, enabling concise, expressive, and compile-time safe branching.
Features
- Generate
Matchextension methods for enums and unions - Exhaustive by design (no missing cases)
- Attribute-driven (opt-in per type)
- Supports generics (
Match<U>) - Respects effective accessibility
- Zero runtime cost (pure source generation)
Getting Started
1. Install the package
dotnet add package Aigamo.MatchGenerator
2. Annotate your type
Enum example
using Aigamo.MatchGenerator;
[GenerateMatch]
public enum Gender
{
Male = 1,
Female,
}
Union example
using Aigamo.MatchGenerator;
[GenerateMatch]
abstract record MaritalStatus;
sealed record Single : MaritalStatus;
sealed record Married : MaritalStatus;
sealed record Divorced : MaritalStatus;
sealed record Widowed : MaritalStatus;
3. Use Match
Enum
var message = gender.Match(
onMale: () => "male",
onFemale: () => "female"
);
Union
var message = maritalStatus.Match(
onSingle: x => "single",
onMarried: x => "married",
onDivorced: x => "divorced",
onWidowed: x => "widowed"
);
Why use MatchGenerator?
Without MatchGenerator
Enum
var message = gender switch
{
Gender.Male => "male",
Gender.Female => "female",
_ => throw new UnreachableException(),
};
Union
var message = maritalStatus switch
{
Single x => "single",
Married x => "married",
Divorced x => "divorced",
Widowed x => "widowed",
_ => throw new UnreachableException(),
};
With MatchGenerator
var message = gender.Match(
onMale: () => "male",
onFemale: () => "female"
);
- More concise
- More readable
- No default case required
- Compile-time safety
Exhaustiveness Guarantee
All cases must be handled.
If a new enum value or union type is added:
public enum Gender
{
Male = 1,
Female,
Other,
}
or
sealed record Separated : MaritalStatus;
Existing Match calls will fail to compile until updated. This ensures no cases are missed.
Generated Code (Example)
Enum
internal static class GenderMatchExtensions
{
public static U Match<U>(
this Gender value,
Func<U> onFemale,
Func<U> onMale
)
{
return value switch
{
Gender.Female => onFemale(),
Gender.Male => onMale(),
_ => throw new UnreachableException(),
};
}
}
Union
internal static class MaritalStatusMatchExtensions
{
public static U Match<U>(
this MaritalStatus value,
Func<Divorced, U> onDivorced,
Func<Married, U> onMarried,
Func<Single, U> onSingle,
Func<Widowed, U> onWidowed
)
{
return value switch
{
Divorced x => onDivorced(x),
Married x => onMarried(x),
Single x => onSingle(x),
Widowed x => onWidowed(x),
_ => throw new UnreachableException(),
};
}
}
References
- Introducing C# Source Generators - .NET Blog
- roslyn/docs/features/source-generators.cookbook.md at main · dotnet/roslyn
- roslyn/docs/features/incremental-generators.cookbook.md at main · dotnet/roslyn
- Domain Modeling Made Functional: Tackle Software Complexity with Domain-Driven Design and F# by Scott Wlaschin
- It Seems the C# Team Is Finally Considering Supporting Discriminated Unions - DEV Community
- salvois/DiscriminatedOnions: A stinky but tasty hack to emulate F#-like discriminated unions in C#