EnumRecords 0.4.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package EnumRecords --version 0.4.0
                    
NuGet\Install-Package EnumRecords -Version 0.4.0
                    
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.4.0">
  <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.4.0" />
                    
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.4.0
                    
#r "nuget: EnumRecords, 0.4.0"
                    
#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.4.0
                    
#: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.4.0
                    
Install as a Cake Addin
#tool nuget:?package=EnumRecords&version=0.4.0
                    
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

Installation

NuGet Package

dotnet add package EnumRecords

Note: EnumRecords is a development-only dependency. NuGet automatically configures it with PrivateAssets="all" so it won't become a transitive dependency of your consumers.

Project Reference

For local development, add a reference to the generator project:

<ItemGroup>
  <ProjectReference Include="path/to/EnumRecords.csproj"
                    OutputItemType="Analyzer"
                    ReferenceOutputAssembly="false" />
</ItemGroup>

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"]

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

Requirements

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

Building from Source

# Clone the repository
git clone https://github.com/dsisco11/EnumRecords.git
cd EnumRecords

# Build the generator
dotnet build EnumRecords.csproj

# Build and run tests
dotnet run --project EnumRecords.Tests/EnumRecords.Tests.csproj

How It Works

  1. Post-Initialization: The generator emits the EnumRecordAttribute<T> and EnumDataAttribute types as source code into your compilation
  2. Syntax Analysis: Finds all enums decorated with [EnumRecord<T>]
  3. Semantic Analysis: Extracts the properties type T and reads constructor parameters to determine property names and types
  4. Code Generation: For each decorated enum, generates a static extension class with one method per property using switch expressions

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