Brainy.Enums 1.0.1

dotnet add package Brainy.Enums --version 1.0.1
                    
NuGet\Install-Package Brainy.Enums -Version 1.0.1
                    
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="Brainy.Enums" Version="1.0.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Brainy.Enums" Version="1.0.1" />
                    
Directory.Packages.props
<PackageReference Include="Brainy.Enums" />
                    
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 Brainy.Enums --version 1.0.1
                    
#r "nuget: Brainy.Enums, 1.0.1"
                    
#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 Brainy.Enums@1.0.1
                    
#: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=Brainy.Enums&version=1.0.1
                    
Install as a Cake Addin
#tool nuget:?package=Brainy.Enums&version=1.0.1
                    
Install as a Cake Tool

Brainy.Enums

A small, dependency-free toolkit of enum operations for C# / .NET 8 — conversions, validation, flags helpers, attribute lookups, and listing utilities — all exposed as simple extension methods.

Reflection is only ever performed once per enum type and cached internally, so every method is fast after the first call.

NuGet .NET 8 License: MIT


Table of Contents


Install

dotnet add package Brainy.Enums

Or via the Package Manager Console:

Install-Package Brainy.Enums

Quick Start

using Brainy.Enums;
using System.ComponentModel;

public enum Status
{
    [Description("Pending Review")]
    Pending = 0,

    [Description("Currently Active")]
    Active = 1,

    Inactive = 2,

    Archived = 3
}
Status s = 1.ToEnum<Status>();          // Status.Active
string d = s.ToDescription();           // "Currently Active"

Everything below assumes using Brainy.Enums; is at the top of the file.


Number ⇄ Enum

Convert between an enum and its underlying numeric value.

int n = Status.Active.ToInt32();      // 1
long l = Status.Active.ToInt64();     // 1
byte b = Status.Active.ToByte();      // 1

Status s1 = 1.ToEnum<Status>();       // Status.Active
Status s2 = 1L.ToEnum<Status>();      // Status.Active (long overload)

ToEnum<T>() throws EnumConversionException if the number doesn't map to a defined member. Use the Try... form when you don't want an exception on invalid input:

if (99.TryToEnum<Status>(out var status))
{
    Console.WriteLine($"Parsed: {status}");
}
else
{
    Console.WriteLine("99 is not a valid Status.");
}

Note: for [Flags] enums, any numeric value is accepted (since valid flag combinations aren't individually "defined" members) — see Flags Enums for stricter validation of flag combinations specifically.


Name (string) ⇄ Enum

Convert between an enum and its declared member name.

string name = Status.Active.ToName();          // "Active"

Status s1 = "Active".ToEnum<Status>();          // Status.Active
Status s2 = "active".ToEnum<Status>(ignoreCase: true); // Status.Active

Throws EnumConversionException for unknown names; use TryToEnum to avoid that:

bool ok = "Bogus".TryToEnum<Status>(out var status, ignoreCase: true);
// ok == false, status == default

Description / Display Name

Reads [Description("...")] (or [Display(Name = "...")] / [DisplayName("...")]) attributes on enum members — handy for showing friendly text in UI instead of the raw member name.

string desc = Status.Active.ToDescription();     // "Currently Active"
string desc2 = Status.Archived.ToDescription();  // "Archived" (falls back to member name — no attribute needed)

Status s = "Pending Review".DescriptionToEnum<Status>(); // Status.Pending

ToDescription() never throws — if no [Description] attribute is present, it falls back to the member name, so it's always safe to call directly in UI-binding code.

ToDisplayName() is the equivalent for [Display] / [DisplayName], and returns null (not a fallback) if the attribute is absent:

string? display = Status.Active.ToDisplayName(); // null unless [Display]/[DisplayName] is present

Validation

bool defined = ((Status)42).IsDefined();  // false
bool defined2 = Status.Active.IsDefined(); // true

Ordering (Next / Previous / Range)

Move through an enum's defined members in declaration order.

Status next = Status.Active.Next();       // Inactive
Status prev = Status.Active.Previous();   // Pending

// Wraps around by default:
Status wrapped = Status.Archived.Next();  // Pending (wraps to the first member)

// Disable wrapping to get an exception at the boundary instead:
Status.Archived.Next(wrap: false);        // throws InvalidOperationException

bool inRange = Status.Active.InRange(Status.Pending, Status.Inactive); // true

Custom Attributes

Fetch any custom attribute declared on an enum member — not just [Description]/[Display].

public enum Status
{
    [MyCustomAttribute(Weight = 10)]
    Active = 1
}

var attr = Status.Active.GetAttribute<Status, MyCustomAttribute>();
int weight = attr?.Weight ?? 0;

Listing & Metadata (EnumInfo<T>)

These are static (there's no instance to hang an extension method off), so they're called as EnumInfo<TEnum>.Method() rather than value.Method().

IReadOnlyList<Status> all    = EnumInfo<Status>.GetValues();       // [Pending, Active, Inactive, Archived]
IReadOnlyList<string> names  = EnumInfo<Status>.GetNames();        // ["Pending", "Active", "Inactive", "Archived"]
IReadOnlyList<string> descs  = EnumInfo<Status>.GetDescriptions(); // ["Pending Review", "Currently Active", ...]

int count    = EnumInfo<Status>.Count;         // 4
Status min   = EnumInfo<Status>.Min;            // Pending
Status max   = EnumInfo<Status>.Max;            // Archived
Type underlying = EnumInfo<Status>.UnderlyingType; // typeof(int)
bool isFlags = EnumInfo<Status>.IsFlagsEnum;    // false

Populating dropdowns / select lists

// Keyed by TEnum - the most common case for server-side binding:
IReadOnlyDictionary<Status, string> byName = EnumInfo<Status>.GetValuesAndNames();
IReadOnlyDictionary<Status, string> byDesc = EnumInfo<Status>.GetValuesAndDescriptions();

foreach (var (value, label) in byDesc)
{
    Console.WriteLine($"<option value=\"{(int)value}\">{label}</option>");
}

When you need a plain numeric or string key instead of TEnum

Useful for JSON APIs, JS interop, HTML form values, or anywhere the consumer can't work with a C# enum type directly.

public enum Priority : byte
{
    [Description("Low Priority")]
    Low = 1,
    Medium = 2,
    [Description("High Priority")]
    High = 3
}

// Key is boxed as the enum's REAL underlying type - here, genuinely `byte`, not `int`:
IReadOnlyDictionary<object, string> byUnderlyingType =
    EnumInfo<Priority>.GetValuesAndNamesByUnderlyingType();
// byUnderlyingType[(byte)2] == "Medium"

IReadOnlyDictionary<object, string> descByUnderlyingType =
    EnumInfo<Priority>.GetValuesAndDescriptionsByUnderlyingType();
// descByUnderlyingType[(byte)1] == "Low Priority"

// Key is the numeric value formatted as a string - handy for HTML <option value="...">,
// query string parameters, or any string-keyed dictionary:
IReadOnlyDictionary<string, string> byStringKey =
    EnumInfo<Priority>.GetValuesAndNamesAsString();
// byStringKey["2"] == "Medium"

IReadOnlyDictionary<string, string> descByStringKey =
    EnumInfo<Priority>.GetValuesAndDescriptionsAsString();
// descByStringKey["1"] == "Low Priority"

Because C# generic methods can't change their return type based on runtime reflection, the ...ByUnderlyingType() methods return IReadOnlyDictionary<object, string> — but each key is genuinely boxed as the enum's real underlying primitive (int, byte, sbyte, short, ushort, uint, long, or ulong), not always boxed as int. A lookup with the wrong boxed numeric type (e.g. a boxed int against a byte-backed enum's dictionary) will correctly miss, since boxed value types only compare equal to boxed values of the exact same type.


Flags Enums

Operations specific to [Flags] enums, in FlagsEnumExtensions.

[Flags]
public enum Permissions
{
    None    = 0,
    Read    = 1,
    Write   = 2,
    Execute = 4,
    Delete  = 8,
    All     = Read | Write | Execute | Delete
}

var value = Permissions.Read | Permissions.Write;

// Faster, non-boxing equivalent of the built-in Enum.HasFlag:
bool canRead = value.HasFlagFast(Permissions.Read);   // true

// Immutable "with" style mutation - each call returns a new value:
value = value.AddFlag(Permissions.Execute);            // Read | Write | Execute
value = value.RemoveFlag(Permissions.Write);            // Read | Execute
value = value.ToggleFlag(Permissions.Delete);            // Read | Execute | Delete

// Decompose a combined value into its individual single-bit flags:
IReadOnlyList<Permissions> setFlags = value.GetSetFlags(); // [Read, Execute, Delete]

// Validate that a value contains only bits that correspond to defined flags
// (catches stray/garbage bits that Enum.IsDefined() wouldn't reliably catch for combinations):
bool valid = value.IsValidFlagsCombination();   // true
bool invalid = ((Permissions)64).IsValidFlagsCombination(); // false - bit 64 isn't a defined flag

Exception Handling

Every "strict" conversion method (ToEnum<T>(), DescriptionToEnum<T>(), Next()/Previous() without wrap) throws Brainy.Enums.EnumConversionException on invalid input, carrying the target enum type and the attempted value:

try
{
    var status = "NotAStatus".ToEnum<Status>();
}
catch (EnumConversionException ex)
{
    Console.WriteLine($"{ex.AttemptedValue} is not valid for {ex.EnumType?.Name}");
}

Every strict method has a matching Try... non-throwing counterpart for validation-heavy or hot-path code where exceptions aren't desirable.


Building from Source

git clone https://github.com/yourname/Brainy.Enums.git
cd Brainy.Enums
dotnet build
dotnet test
dotnet pack src/Brainy.Enums/Brainy.Enums.csproj -c Release -o ./artifacts
dotnet nuget push ./artifacts/Brainy.Enums.1.0.0.nupkg \
  --api-key <YOUR_NUGET_API_KEY> \
  --source https://api.nuget.org/v3/index.json

Project layout

Brainy.Enums.sln
src/Brainy.Enums/
  EnumExtensions.cs             # number/name/description conversions, validation, ordering, attributes
  EnumInfo.cs                   # static listing/metadata (GetValues, GetNames, Min, Max, dropdown helpers, ...)
  FlagsEnumExtensions.cs        # [Flags]-specific helpers
  EnumConversionException.cs
  Internal/EnumCache.cs         # one-time reflection cache per enum type
tests/Brainy.Enums.Tests/       # xUnit test suite

Design Notes

  • Every conversion method has both a throwing form (e.g. ToEnum<T>()) and a non-throwing Try... form, so you can pick the right style for hot paths vs. validation paths.
  • HasFlagFast avoids the boxing allocation that the built-in Enum.HasFlag incurs on every call.
  • ToDescription() always falls back to the member name when no [Description] attribute is present, so it's safe to call unconditionally in display code.
  • Reflection is performed exactly once per enum type (cached in EnumCache<T>), so repeated calls are just dictionary/array lookups.
  • The library is reflection-based under the hood, which makes it very convenient but not currently trimming / Native AOT safe. A source-generator-based version is a natural next step if you need that.

Contributing

Contributions, ideas, feature requests, and bug reports are always welcome.

If you'd like to improve Brainy.Enums, feel free to open an issue or submit a pull request.


Author

Brainy.Mediator is created and maintained by Shakeel Iqbal, a Senior .NET Architect and C# Developer with extensive experience building enterprise applications and software solutions using the Microsoft technology stack.


License

This project is licensed under the MIT License.


About Brainy Solutions

We build modern software solutions using .NET, cloud technologies, AI, and enterprise architecture. As part of our commitment to the developer community, we actively open-source tools and libraries that help simplify software development.

If you find Brainy.Mediator

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.
  • net8.0

    • No dependencies.
  • net9.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
1.0.1 112 8/11/2026
1.0.0 98 8/11/2026