SkillAgentSearch skills...

NetEscapades.EnumGenerators

A source generator for generating fast "reflection" methods for enums

Install / Use

/learn @andrewlock/NetEscapades.EnumGenerators
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

<!-- GENERATED FILE - DO NOT EDIT This file was generated by [MarkdownSnippets](https://github.com/SimonCropp/MarkdownSnippets). Source File: /README.source.md To change this file edit the source file and then run MarkdownSnippets. -->

NetEscapades.EnumGenerators

Build status NuGet

NetEscapades.EnumGenerators requires the .NET 7 SDK or higher. NetEscapades.EnumGenerators.Interceptors is experimental and requires the .NET 8.0.400 SDK or higher. You can still target earlier frameworks like .NET Core 3.1 etc, the version requirement only applies to the version of the .NET SDK installed.

NetEscapades.EnumGenerators is a metapackage, see below for details about the associated packages and other options for packages to reference.

<!-- toc -->

Contents

Why use these packages?

Many methods that work with enums are surprisingly slow. Calling ToString() or HasFlag() on an enum seems like it should be fast, but it often isn't. This package provides a set of extension methods, such as ToStringFast() or HasFlagFast() that are designed to be very fast, with fewer allocations.<!-- include: benchmark. path: /fragments/benchmark.include.md -->

For example, the following benchmark shows the advantage of calling ToStringFast() over ToString():

BenchmarkDotNet=v0.13.1, OS=Windows 10.0.19042.1348 (20H2/October2020Update)
Intel Core i7-7500U CPU 2.70GHz (Kaby Lake), 1 CPU, 4 logical and 2 physical cores
  DefaultJob : .NET Framework 4.8 (4.8.4420.0), X64 RyuJIT
.NET SDK=6.0.100
  DefaultJob : .NET 6.0.0 (6.0.21.52210), X64 RyuJIT

| Method | FX | Mean | Error | StdDev | Ratio | Gen 0 | Allocated | |------------- |-----------|-----------:|----------:|------------:|------:|-------:|----------:| | ToString | net48 | 578.276 ns | 3.3109 ns | 3.0970 ns | 1.000 | 0.0458 | 96 B | | ToStringFast | net48 | 3.091 ns | 0.0567 ns | 0.0443 ns | 0.005 | - | - | | ToString | net6.0 | 17.985 ns | 0.1230 ns | 0.1151 ns | 1.000 | 0.0115 | 24 B | | ToStringFast | net6.0 | 0.121 ns | 0.0225 ns | 0.0199 ns | 0.007 | - | - | | ToString | net10.0 | 6.4389 ns | 0.1038 ns | 0.0971 ns | 0.004 | 1.000 | 24 B | | ToStringFast | net10.0 | 0.0050 ns | 0.0202 ns | 0.0189 ns | 0.001 | - | - |

Enabling these additional extension methods is as simple as adding an attribute to your enum:

[EnumExtensions] // 👈 Add this
public enum Color
{
    Red = 0,
    Blue = 1,
}
<!-- endInclude -->

Adding NetEscapades.EnumGenerators to your project

Add the package to your application using

dotnet add package NetEscapades.EnumGenerators

This adds a <PackageReference> to your project:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
  </PropertyGroup>

  <!-- Add the package -->
  <PackageReference Include="NetEscapades.EnumGenerators" Version="1.0.0-beta21" />
  <!-- -->

</Project>

[!WARNING] You should not use PrivateAssets when referencing the NetEscapades.EnumGenerators package, as the package has runtime dependencies. If you wish to avoid these runtime dependencies, see below for alternative approaches.

Adding the package will automatically add a marker attribute, [EnumExtensions], to your project.<!-- include: enum-usage. path: /fragments/enum-usage.include.md -->

To use the generator, add the [EnumExtensions] attribute to an enum. For example:

[EnumExtensions]
public enum MyEnum
{
    First,

    [EnumMember(Value = "2nd")]
    Second,
}

This will generate a class called MyEnumExtensions (by default), which contains a number of helper methods. For example:

public static partial class MyEnumExtensions
{
    public const int Length = 2;

    public static string ToStringFast(this MyEnum value, bool useMetadataAttributes)
        => useMetadataAttributes ? value.ToStringFastWithMetadata() : value.ToStringFast();

    public static string ToStringFast(this MyEnum value)
        => value switch
        {
            MyEnum.First => nameof(MyEnum.First),
            MyEnum.Second => nameof(MyEnum.Second),
            _ => value.ToString(),
        };

    private static string ToStringFastWithMetadata(this MyEnum value)
        => value switch
        {
            MyEnum.First => nameof(MyEnum.First),
            MyEnum.Second => "2nd",
            _ => value.ToString(),
        };

    public static bool IsDefined(MyEnum value)
        => value switch
        {
            MyEnum.First => true,
            MyEnum.Second => true,
            _ => false,
        };

    public static bool IsDefined(string name) => IsDefined(name, allowMatchingMetadataAttribute: false);

    public static bool IsDefined(string name, bool allowMatchingMetadataAttribute)
    {
        var isDefinedInDisplayAttribute = false;
        if (allowMatchingMetadataAttribute)
        {
            isDefinedInDisplayAttribute = name switch
            {
                "2nd" => true,
                _ => false,
            };
        }

        if (isDefinedInDisplayAttribute)
        {
            return true;
        }


        return name switch
        {
            nameof(MyEnum.First) => true,
            nameof(MyEnum.Second) => true,
            _ => false,
        };
    }

    public static MyEnum Parse(string? name)
        => TryParse(name, out var value, false, false) ? value : ThrowValueNotFound(name);

    public static MyEnum Parse(string? name, bool ignoreCase)
        => TryParse(name, out var value, ignoreCase, false) ? value : ThrowValueNotFound(name);

    public static MyEnum Parse(string? name, bool ignoreCase, bool allowMatchingMetadataAttribute)
        => TryParse(name, out var value, ignoreCase, allowMatchingMetadataAttribute) ? value : throw new ArgumentException($"Requested value '{name}' was not found.");

    public static bool TryParse(string? name, out MyEnum value)
        => TryParse(name, out value, false, false);

    public static bool TryParse(string? name, out MyEnum value, bool ignoreCase)
        => TryParse(name, out value, ignoreCase, false);

    public static bool TryParse(string? name, out MyEnum value, bool ignoreCase, bool allowMatchingMetadataAttribute)
        => ignoreCase
            ? TryParseIgnoreCase(name, out value, allowMatchingMetadataAttribute)
            : TryParseWithCase(name, out value, allowMatchingMetadataAttribute);

    private static bool TryParseIgnoreCase(string? name, out MyEnum value, bool allowMatchingMetadataAttribute)
    {
        if (allowMatchingMetadataAttribute)
        {
            switch (name)
            {
                case string s when s.Equals("2nd", System.StringComparison.OrdinalIgnoreCase):
                    value = MyEnum.Second;
                    return true;
                default:
                    break;
            };
        }

        switch (name)
        {
            case string s when s.Equals(nameof(MyEnum.First), System.StringComparison.OrdinalIgnoreCase):
                value = MyEnum.First;
                return true;
            case string s when s.Equals(nameof(MyEnum.Second), System.StringComparison.OrdinalIgnoreCase):
                value = MyEnum.Second;
                return true;
            case string s when int.TryParse(name, out var val):
                value = (MyEnum)val;
                return true;
            default:
                value = default;
                return false;
        }
    }

    private static bool TryParseWithCase(string? name, out MyEnum value, bool allowMatchingMetadataAttribute)
    {
        if (allowMatchingMetadataAttribute)
        {
            switch (name)
       
View on GitHub
GitHub Stars1.1k
CategoryDevelopment
Updated3d ago
Forks55

Languages

C#

Security Score

95/100

Audited on Mar 28, 2026

No findings