High performance source generated enum values.
$ dotnet add package Soenneker.Gen.EnumValues
Soenneker.Gen.EnumValuesGenerate value objects with fast lookup APIs, switch-friendly constants, and built-in JSON serialization (System.Text.Json/Newtonsoft.Json).
dotnet add package Soenneker.Gen.EnumValues
Annotate a partial type with [EnumValue] (defaults to int) or [EnumValue<T>]:
using Soenneker.Gen.EnumValues;
[EnumValue]
public sealed partial class OrderStatus
{
public static readonly OrderStatus Pending = new(1);
public static readonly OrderStatus Completed = new(2);
}
using Soenneker.Gen.EnumValues;
[EnumValue<string>]
public sealed partial class ColorCode
{
public static readonly ColorCode Red = new("R");
public static readonly ColorCode Blue = new("B");
}
The generator emits:
List<MemberName>Value constants (for constant-friendly switch labels)TryFromValue(TValue value, out TEnum result)FromValue(TValue value)TryFromName(string name, out TEnum result)FromName(string name)if (OrderStatus.TryFromValue(1, out var pending))
{
// pending == OrderStatus.Pending
}
var completed = OrderStatus.FromValue(2);
if (ColorCode.TryFromName("Red", out var red))
{
// red == ColorCode.Red
}
Switch labels must be compile-time constants. The generator emits <MemberName>Value constants so you can switch efficiently on Value:
switch (orderStatus.Value)
{
case OrderStatus.PendingValue:
// ...
break;
case OrderStatus.CompletedValue:
// ...
break;
}
If your variable is already the raw value type (int, string, etc.), you can switch directly on that variable with the same constants.
System.Text.Json is always supported and the converter is applied automatically.
Newtonsoft.Json is also supported automatically when your project references Newtonsoft.Json:
dotnet add package Newtonsoft.Json
After that, both serializers round-trip by Value.
Value and the value constructor are generated automatically if they do not already exist.
[IncludeEnumValues]You can reuse instances from another enum-value type by adding [IncludeEnumValues(typeof(SourceType))]. The generator merges your type’s own static instances with all instances from the source type. Order is deterministic: your own instances first (source order), then each included type’s instances in attribute order and source order.
Example: source type
[EnumValue<string>]
public sealed partial class CommonKeyword
{
public static readonly CommonKeyword Default = new("default");
public static readonly CommonKeyword Auto = new("auto");
public static readonly CommonKeyword None = new("none");
}
Example: composed type
[EnumValue<string>]
[IncludeEnumValues(typeof(CommonKeyword))]
public sealed partial class SortDirection
{
public static readonly SortDirection Ascending = new("asc");
public static readonly SortDirection Descending = new("desc");
}
SortDirection then has five instances: Ascending, Descending (own), then Default, Auto, None (from CommonKeyword). The generator emits static readonly fields for included instances (e.g. SortDirection.Default), so List, Values, TryFromValue, TryFromName, and JSON work for both own and included values.
Requirements
[EnumValue] or [EnumValue<T>] with the same value type as the target (e.g. both [EnumValue<string>]).[IncludeEnumValues(typeof(A))], [IncludeEnumValues(typeof(B))]; instances are merged in attribute order.Collisions
partial.<MemberName>Value constants are emitted for const-compatible value types (for example: numeric types, string, char, bool).