EnumRecords 0.5.2

dotnet add package EnumRecords --version 0.5.2
                    
NuGet\Install-Package EnumRecords -Version 0.5.2
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="EnumRecords" Version="0.5.2">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="EnumRecords" Version="0.5.2" />
                    
Directory.Packages.props
<PackageReference Include="EnumRecords">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add EnumRecords --version 0.5.2
                    
#r "nuget: EnumRecords, 0.5.2"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package EnumRecords@0.5.2
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=EnumRecords&version=0.5.2
                    
Install as a Cake Addin
#tool nuget:?package=EnumRecords&version=0.5.2
                    
Install as a Cake Tool

EnumRecords

A C# source generator that associates compile-time constant data properties with enum values, enabling property-like access via generated extension methods.

CI

Features

  • 🚀 Zero runtime overhead — All code is generated at compile time
  • 📦 No runtime dependencies — Attributes are source-generated into your project
  • 🔍 IntelliSense support — Full IDE autocomplete for generated extension methods
  • Type-safe — Compile-time validation of property types and values
  • 🎯 Simple API — Just two attributes to learn
  • 🔄 Reverse lookup — Find enum values by property values with [ReverseLookup]
  • 📋 Collection access — Get all property values via Get{PropertyName}s() methods

Requirements

  • .NET 6.0+ or .NET Standard 2.0+ consuming project
  • C# 9.0+ (for record struct support in consuming code)

Quick Start

1. Define a Properties Record Struct

Create a readonly record struct that defines the schema for your enum's associated data:

public readonly record struct ColorEnumProperties(
    string Name,
    int Value,
    string HexCode
);

2. Decorate Your Enum

Apply [EnumRecord<T>] to your enum and [EnumData(...)] to each member:

using EnumRecords;

[EnumRecord<ColorEnumProperties>]
public enum EColors : int
{
    [EnumData("Red", 1, "#FF0000")]
    Red = 1,

    [EnumData("Green", 2, "#00FF00")]
    Green = 2,

    [EnumData("Blue", 3, "#0000FF")]
    Blue = 3,
}

3. Access Properties via Extension Methods

The generator creates extension methods for each property in your record struct:

// Access properties like methods on enum values
string hex = EColors.Red.HexCode();      // "#FF0000"
string name = EColors.Green.Name();      // "Green"
int value = EColors.Blue.Value();        // 3

// Works with variables too
EColors color = EColors.Red;
Console.WriteLine(color.HexCode());      // "#FF0000"

// Iterate over all values
foreach (EColors c in Enum.GetValues<EColors>())
{
    Console.WriteLine($"{c}: {c.Name()} - {c.HexCode()}");
}

Supported Property Types

The [EnumData] attribute accepts any compile-time constant values:

Type Example
string "Hello"
int, long, short, byte 42, 100L
uint, ulong, ushort, sbyte 42U, 100UL
float, double, decimal 3.14f, 3.14d, 3.14m
bool true, false
char 'A'

Reverse Lookup

You can mark properties with [ReverseLookup] to generate methods that find an enum value by its property value:

Setup

Add [ReverseLookup] to the constructor parameter in your properties record struct:

public readonly record struct ColorEnumProperties(
    string Name,
    int Value,
    [ReverseLookup] string HexCode  // Enable reverse lookup for HexCode
);

Generated Methods

For each property marked with [ReverseLookup], two static methods are generated:

// Non-throwing variant - returns false if not found
public static bool TryFromHexCode(string value, out EColors result);

// Throwing variant - throws ArgumentException if not found
public static EColors FromHexCode(string value);

Usage

// Find enum by property value (throwing)
EColors red = EColorsExtensions.FromHexCode("#FF0000");  // Returns EColors.Red

// Find enum by property value (non-throwing)
if (EColorsExtensions.TryFromHexCode("#00FF00", out var color))
{
    Console.WriteLine(color);  // Green
}

// Handle not found
if (!EColorsExtensions.TryFromHexCode("#FFFFFF", out _))
{
    Console.WriteLine("Color not found");
}

// Throwing variant for unknown values
try
{
    var unknown = EColorsExtensions.FromHexCode("#FFFFFF");
}
catch (ArgumentException ex)
{
    // "No EColors found with HexCode '#FFFFFF'"
}

Uniqueness Requirement

Properties marked with [ReverseLookup] must have unique values across all enum members. The generator emits a compile-time error (ENUMREC001) if duplicate values are detected:

// ❌ This will cause compile error ENUMREC001
public readonly record struct BadProps([ReverseLookup] string Code);

[EnumRecord<BadProps>]
public enum BadEnum
{
    [EnumData("A")]
    First,
    [EnumData("A")]  // Error: Duplicate value '"A"' for reverse-lookup property 'Code'
    Second,
}

Case-Insensitive String Lookup

For string properties, you can enable case-insensitive lookups with IgnoreCase = true:

public readonly record struct FileTypeProperties(
    string Extension,
    [ReverseLookup(IgnoreCase = true)] string MimeType
);

[EnumRecord<FileTypeProperties>]
public enum FileType
{
    [EnumData(".json", "application/json")]
    Json,
    [EnumData(".xml", "application/xml")]
    Xml,
}

// Usage - all these will match FileType.Json
FileTypeExtensions.FromMimeType("application/json");  // exact match
FileTypeExtensions.FromMimeType("APPLICATION/JSON");  // uppercase
FileTypeExtensions.FromMimeType("Application/Json");  // mixed case

Note: When using IgnoreCase = true, the uniqueness check also uses case-insensitive comparison. For example, "ABC" and "abc" would be considered duplicates.

Ignoring Enum Members

Use [Ignore] to exclude specific enum members from property mappings. This is useful for sentinel values like None, Unknown, or deprecated entries.

Setup

using EnumRecords;

[EnumRecord<FileTypeProperties>]
public enum FileType
{
    [Ignore]  // No properties required, excluded from all generated code
    Unknown = 0,

    [EnumData(".json", "application/json")]
    Json,

    [EnumData(".xml", "application/xml")]
    Xml,
}

Behavior

Members marked with [Ignore]:

  • Do not require [EnumData] — no compile error for missing properties
  • Are excluded from generated extension methods — calling .Extension() on an ignored member throws ArgumentOutOfRangeException
  • Are excluded from Get{PropertyName}s() collections
  • Are excluded from reverse lookup methods
// Ignored members throw when accessing properties
try
{
    var ext = FileType.Unknown.Extension();
}
catch (ArgumentOutOfRangeException)
{
    // Expected - Unknown is not mapped
}

// Ignored members are not in collections
var extensions = FileTypeExtensions.GetExtensions();  // [".json", ".xml"] - no Unknown

// Reverse lookup won't return ignored members
FileTypeExtensions.TryFromMimeType("unknown", out _);  // false

Note: If you have a conflict with another [Ignore] attribute (e.g., from NUnit or MSTest), use the fully qualified name: [EnumRecords.Ignore]

Get All Property Values

For each property in your record struct, the generator creates a Get{PropertyName}s() method that returns all defined values as a read-only list:

Generated Methods

// For ColorEnumProperties with Name, Value, and HexCode properties:
public static IReadOnlyList<string> GetNames();
public static IReadOnlyList<int> GetValues();
public static IReadOnlyList<string> GetHexCodes();

Usage

// Get all property values as collections
var names = EColorsExtensions.GetNames();       // ["Red", "Green", "Blue"]
var values = EColorsExtensions.GetValues();     // [1, 2, 3]
var hexCodes = EColorsExtensions.GetHexCodes(); // ["#FF0000", "#00FF00", "#0000FF"]

// Useful for validation, dropdowns, etc.
if (EColorsExtensions.GetHexCodes().Contains(userInput))
{
    // Valid hex code
}

// Or with FileType enum
var extensions = FileTypeExtensions.GetExtensions();   // [".json", ".xml", ".csv"]
var mimeTypes = FileTypeExtensions.GetMimeTypes();     // ["application/json", "application/xml", "text/csv"]

Static Record Helper Classes

In addition to extension methods, the generator creates static helper classes for direct method calls without extension method syntax.

Per-Enum Record Class

For each enum with [EnumRecord<T>], a static {EnumName}Record class is generated with all the logic:

// Generated for EColors enum - contains actual implementation
public static class EColorsRecord
{
    public static string GetName(EColors value) => value switch { ... };
    public static int GetValue(EColors value) => value switch { ... };
    public static string GetHexCode(EColors value) => value switch { ... };

    public static IReadOnlyList<string> GetNames() => new[] { "Red", "Green", "Blue" };
    public static IReadOnlyList<int> GetValues() => new[] { 1, 2, 3 };
    public static IReadOnlyList<string> GetHexCodes() => new[] { "#FF0000", "#00FF00", "#0000FF" };

    // If [ReverseLookup] is used:
    public static bool TryFromHexCode(string value, [NotNullWhen(true)] out EColors? result) => ...;
    public static EColors FromHexCode(string value) => ...;
}

The extension methods in {EnumName}Extensions forward to these static methods:

// Extension methods delegate to the record class
public static class EColorsExtensions
{
    public static string Name(this EColors value) => EColorsRecord.GetName(value);
    public static string HexCode(this EColors value) => EColorsRecord.GetHexCode(value);
    // ...
}

Central EnumRecord Lookup

A static EnumRecord class provides a central namespace for all enum helpers via nested classes:

// Access via nested classes
string name = EnumRecord.EColors.GetName(EColors.Red);  // "Red"
var allNames = EnumRecord.EColors.GetNames();           // ["Red", "Green", "Blue"]

// Reverse lookups
if (EnumRecord.EColors.TryFromHexCode("#FF0000", out var color))
{
    Console.WriteLine(color);  // Red
}

Use Cases

Static record classes are useful when you need to:

  • Avoid extension method syntax — use explicit static method calls
  • Work with generic code — use the helper in scenarios where extension methods are awkward
  • Centralized access — use EnumRecord.{EnumName}.Method() for a unified namespace
// Direct static access without extension syntax
string name = EColorsRecord.GetName(EColors.Red);  // "Red"

// Central namespace access
var hexCodes = EnumRecord.EColors.GetHexCodes();

// Useful in LINQ or generic contexts
var colors = Enum.GetValues<EColors>()
    .Select(c => new { Color = c, Hex = EColorsRecord.GetHexCode(c) });

Advanced Examples

HTTP Status Codes

public readonly record struct HttpStatusProperties(
    int Code,
    string Phrase,
    bool IsSuccess
);

[EnumRecord<HttpStatusProperties>]
public enum HttpStatus
{
    [EnumData(200, "OK", true)]
    Ok = 200,

    [EnumData(201, "Created", true)]
    Created = 201,

    [EnumData(400, "Bad Request", false)]
    BadRequest = 400,

    [EnumData(404, "Not Found", false)]
    NotFound = 404,

    [EnumData(500, "Internal Server Error", false)]
    InternalServerError = 500,
}

// Usage
if (HttpStatus.Ok.IsSuccess())
{
    Console.WriteLine(HttpStatus.Ok.Phrase()); // "OK"
}

File Types

public readonly record struct FileTypeProperties(
    string Extension,
    string MimeType,
    string Description
);

[EnumRecord<FileTypeProperties>]
public enum FileType
{
    [EnumData(".json", "application/json", "JSON Document")]
    Json,

    [EnumData(".xml", "application/xml", "XML Document")]
    Xml,

    [EnumData(".csv", "text/csv", "Comma-Separated Values")]
    Csv,
}

// Usage
string mime = FileType.Json.MimeType(); // "application/json"

API Reference

EnumRecordAttribute<TProperties>

Marks an enum as having associated record properties.

[EnumRecord<TProperties>]
public enum MyEnum { ... }
  • TProperties must be a struct (typically a readonly record struct)
  • Applied to the enum declaration

EnumDataAttribute

Specifies the property values for an enum member.

[EnumData(arg1, arg2, ...)]
EnumMember = value,
  • Arguments are positional and must match the order of the properties record struct's constructor parameters
  • All arguments must be compile-time constants

ReverseLookupAttribute

Marks a property for reverse lookup, enabling lookup of enum values by property value.

public readonly record struct MyProperties([ReverseLookup] string UniqueId);
  • Applied to constructor parameters of the properties record struct
  • Property values must be unique across all enum members (enforced at compile time)
  • Generates TryFrom{PropertyName} and From{PropertyName} static methods

Properties:

Property Type Default Description
IgnoreCase bool false Enable case-insensitive matching for string lookups

IgnoreAttribute

Excludes an enum member from property mappings and generated code.

[Ignore]
EnumMember = value,
  • Applied to enum members (fields)
  • Member does not require [EnumData]
  • Excluded from extension methods, collections, and reverse lookups

Generated Classes

For each enum decorated with [EnumRecord<T>], the generator produces:

Generated Type Description
{EnumName}Extensions Static extension methods that delegate to {EnumName}Record
{EnumName}Record Static class containing the actual implementation logic
EnumRecord (once) Central static class with nested classes for each enum, forwarding to record class

Nullability Attributes

Generated TryFrom* methods include proper nullability annotations:

public static bool TryFromHexCode(
    string value,
    [NotNullWhen(true)] out EColors? result);
  • [NotNullWhen(true)] indicates that result is non-null when the method returns true
  • The out parameter is nullable (EColors?) and returns null on lookup failure

License

This project is licensed under the MIT License - see the LICENSE.txt file for details.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

There are no supported framework assets in this package.

Learn more about Target Frameworks and .NET Standard.

  • .NETStandard 2.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.5.2 195 12/26/2025
0.5.1 183 12/26/2025
0.5.0 186 12/26/2025
0.4.1 201 12/26/2025
0.4.0 206 12/25/2025
0.3.0 194 12/25/2025
0.2.0 201 12/25/2025
0.1.1 193 12/24/2025