CodeChops.MagicEnums
4.0.0
See the version list below for details.
dotnet add package CodeChops.MagicEnums --version 4.0.0
NuGet\Install-Package CodeChops.MagicEnums -Version 4.0.0
<PackageReference Include="CodeChops.MagicEnums" Version="4.0.0" />
paket add CodeChops.MagicEnums --version 4.0.0
#r "nuget: CodeChops.MagicEnums, 4.0.0"
// Install CodeChops.MagicEnums as a Cake Addin #addin nuget:?package=CodeChops.MagicEnums&version=4.0.0 // Install CodeChops.MagicEnums as a Cake Tool #tool nuget:?package=CodeChops.MagicEnums&version=4.0.0
Magic Enums
Customizable, and extendable enums for C# with a clean API.
Check out CodeChops projects for more projects.
Examples
Simple enum
// Implicit values
public record Level : MagicEnum<Level>
{
public static readonly Level Low = CreateMember(); // 0
public static readonly Level Medium = CreateMember(); // 1
public static readonly Level High = CreateMember(); // 2
}
// Explicit values
public record StarRating : MagicEnum<StarRating>
{
public static readonly StarRating One = CreateMember(1);
public static readonly StarRating Two = CreateMember();
public static readonly StarRating Three = CreateMember();
public static readonly StarRating Four = CreateMember();
public static readonly StarRating Five = CreateMember();
}
Complex enum
public record Vehicle(int WheelCount) : MagicEnum<Vehicle>
{
public static readonly Type.Bicycle Bicycle = CreateMember<Type.Bicycle>();
public static readonly Type.MotorCycle MotorCycle = CreateMember<Type.MotorCycle>();
public static readonly Type.Car FuelCar = CreateMember<Type.Car>(() => new(EmitsCo2: true));
public static readonly Type.Car ElectricCar = CreateMember<Type.Car>(() => new(EmitsCo2: false));
public static class Type
{
public record Bicycle() : Vehicle(WheelCount: 2);
public record MotorCycle() : Vehicle(WheelCount: 2);
public record Car(bool EmitsCo2) : Vehicle(WheelCount: 4);
}
}
Advantages
Besides the default .NET enum behaviour, MagicEnums offer more features than the default .NET enum implementation:
- Fast and optimized: does not use reflection!
- Extendability:
- Inheritance is supported. This way enums can also be extended in other assemblies.
- Partial enums are supported.
- Custom methods and properties can be added to the enums.
- Different types of enums are supported:
- Number enums (including
decimal
) - Flags enums
- String enums
- Custom enums
- Number enums (including
- Members can be added at runtime, if necessary. This is thread-safe.
- Members with the same value can be looked up easily. Something which is not supported in default C# enums.
- Optimized, and therefore fast member registration and lookup, including a fast
ToString
. For extra optimization, see optimization. - Serialization to/from JSON is supported.
- Members can be auto-discovered. This removes the need to keep track of used/unused enum-members. See auto member discoverability.
Functionality
Terminology:
- An
enum
has one or multiple members.- Each
member
has aname
and avalue
.- The type of the value depends on the chosen
enum type
, see: enum types.
Magic enums behave like the default .NET enum implementation:
- They use
int
as default for the member value. - Members can be found by searching for their name or value.
- More than one member can have the same value but not the same name.
- The value of members can be omitted (when using the default numeric enums). If omitted, it automatically increments the value of each member.
- Members, member names or member values can easily be enumerated.
Flag
enums are supported.- Strongly typed enum members, so pattern matching can be used.
Usage
- Add the package
CodeChops.MagicEnums
. - Add a (global) using to namespace
CodeChops.MagicEnums
. - Create a record which implements one of the enum types.
- Add members by creating
static readonly
members that callCreateMember()
.
API
Method | Description |
---|---|
CreateMember * |
Creates a new enum member and returns it. |
GetEnumerator |
Gets an enumerator over the enum members. |
GetMembers |
Gets an enumerable over:<br/>- All enum members, or<br/>- Members of a specific value: Throws when no member has been found. |
GetValues |
Gets an enumerable over the member values. |
TryGetMembers |
Tries to get member(s) by value. |
TryGetSingleMember |
Tries to get a single member by name / value.<br/>Throws when multiple members of the same value have been found. |
GetSingleMember |
Gets a single member by name / value.<br/>Throws when not found or multiple members have been found. |
GetUniqueValueCount |
Gets the unique member value count. |
GetMemberCount |
Gets the member count. |
GetDefaultValue |
Gets the default value of the enum. |
GetOrCreateMember * |
Creates a member or gets one if a member already exists. |
The methods are
public
, except for the ones marked with *, they areprotected
.
Enum types
Number enums
Number enums (default) have a numeric type as value.
- Can be created by implementing
MagicEnum<TSelf, TNumber>
. - If
TNumber
is omitted,int
will be used as type:MagicEnum<TSelf>
. TNumber
can be of any type that are also supported by the default .NET implementation:byte
,sbyte
,short
,ushort
,int
,uint
,long
, orulong
.- Unlike the default C# .NET implementation,
decimal
is also supported. - Implicit and explicit value declaration are supported, see the example below.
/* This example shows an int enum with implicit and explicit values. */
public record StarRating : MagicEnum<StarRating>
{
public static readonly StarRating One = CreateMember(1);
public static readonly StarRating Two = CreateMember();
public static readonly StarRating Three = CreateMember();
public static readonly StarRating Four = CreateMember();
public static readonly StarRating Five = CreateMember();
}
The example creates an enum with an int
as member value.
The value of the first member is explicitly defined.
Other values are being incremented automatically, because they are defined implicitly.
Example
/* This example shows the usage of a `decimal` as member value. */
public record EurUsdRate : MagicEnum<EurUsdRate, decimal>
{
public static readonly EurUsdRate Average2021 = CreateMember(0.846m);
public static readonly EurUsdRate Average2020 = CreateMember(0.877m);
public static readonly EurUsdRate Average2019 = CreateMember(0.893m);
public static readonly EurUsdRate Average2018 = CreateMember(0.848m);
}
Flags enums
Flag enums are supported just like in the default .NET implementation. Implicit value declaration is also support. See the example below. Flags enums offer extra methods:
GetUniqueFlags()
: Gets the unique flags of the provided value.HasFlag()
: Returnstrue
if a specific enum member contains the provided flag.
Example
/* This example shows the usage of a flags enum.
Note that member 'ReadAndWrite' flags both 'Read' and 'Write'. */
public record Permission : MagicFlagsEnum<Permission>
{
public static readonly Permission None = CreateMember(); // 0
public static readonly Permission Read = CreateMember(); // 1 << 0
public static readonly Permission Write = CreateMember(); // 1 << 1
public static readonly Permission ReadAndWrite = CreateMember(Read | Write);
}
String enums
Sometimes you only need an enumeration of strings
(for example: names). In this case the underlying numeric value is not important. Magic string enums helps you achieving this:
- Can be created by implementing
MagicStringEnum<TSelf>
. - Ensure that the values of the members are equal to the name of the members. This can be manually overriden, if necessary.
- Prohibit incorrect usage of numeric values when they are not needed.
- Remove the need to keep track of (incremental) numeric values.
- When members are generated, they show an automatic warning in the comments that the members shouldn't be renamed. See member generation.
Example
/* This example shows the creation of a string enum.
The value of the members are equal to the name of the members. */
using CodeChops.MagicEnums;
public record ErrorCode : MagicStringEnum<ErrorCode>
{
public static readonly ErrorCode EndpointDoesNotExist = CreateMember();
public static readonly ErrorCode InvalidParameters = CreateMember();
public static readonly ErrorCode NotAuthorized = CreateMember();
}
Custom enums
Custom enums can also be created. They offer a way to create an enum of any type that you prefer:
- Can be created by implementing
MagicCustomEnum<TSelf, TValue>
. TValue
should implementIEquatable
andIComparable
.- A custom value type can easily be generated using the Value object generator which is included in the Domain Modeling-library.
- Two dogfooding examples of the usage custom enums:
- See Strict direction modes in the Geometry library. It contains enums that have a
2D-point
as member value. - See Implementation discovery, which automatically creates an enum of every implementation of a specific
base class
/interface
. Each member contains its (uninitialized) instance as value.
- See Strict direction modes in the Geometry library. It contains enums that have a
Pattern matching
To achieve pattern matching, you can do the following below.
var message = level.Name switch
{
nameof(Level.Low) => "The level is low.",
nameof(Level.Medium) => "The level is medium.",
nameof(Level.High) => "The level is high.",
_ => throw new UnreachableException($"This should not occur.")
};
In this example, the enum from the default usage example is used.
Another way is to define the types in an inner class and use them as the type of an enum member:
var speedingFineInEur = vehicle switch
{
Vehicle.Type.MotorCycle => 60,
Vehicle.Type.Car => 100,
_ => 0,
};
In this example, the enum from the complex usage example is used.
Optimization
Generally your enum does not dynamically add members at runtime. If this is the case, the attribute DisableConcurrency
can be placed on the enum. It disables concurrency and therefore optimises memory usage and speed.
Warning! Only use this attribute when you are sure that no race conditions can take place when creating / reading members.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | 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. |
-
net9.0
- Architect.DomainModeling (>= 3.0.3)
NuGet packages (2)
Showing the top 2 NuGet packages that depend on CodeChops.MagicEnums:
Package | Downloads |
---|---|
CodeChops.ImplementationDiscovery
Provides easy-accessible, design-time and runtime information about implementations throughout your code. |
|
CodeChops.MagicEnums.Json
Json (de)serialization for MagicEnums. |
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
4.0.1 | 47 | 2/21/2025 |
4.0.0 | 41 | 2/21/2025 |
3.9.3-alpha | 714 | 12/11/2024 |
3.9.2 | 1,313 | 10/16/2024 |
3.9.1 | 229 | 9/29/2024 |
3.9.0 | 644 | 3/20/2023 |
3.8.3 | 434 | 3/10/2023 |
3.8.2 | 455 | 3/6/2023 |
3.8.0 | 422 | 1/27/2023 |
3.7.1 | 414 | 1/22/2023 |
3.6.0 | 865 | 1/7/2023 |
3.5.0 | 422 | 1/6/2023 |
3.4.4 | 535 | 1/6/2023 |
3.4.2 | 375 | 1/4/2023 |
3.4.1 | 464 | 1/3/2023 |
3.4.0 | 389 | 1/2/2023 |
3.3.9 | 338 | 1/2/2023 |
3.3.5 | 405 | 12/23/2022 |
3.3.4 | 353 | 12/22/2022 |
3.3.3 | 329 | 12/19/2022 |
3.3.2 | 371 | 12/16/2022 |
3.3.1 | 366 | 12/15/2022 |
3.3.0 | 345 | 12/14/2022 |
2.9.9 | 600 | 9/17/2022 |
2.9.8 | 584 | 9/16/2022 |
2.9.5 | 599 | 9/16/2022 |
2.9.4 | 477 | 9/15/2022 |
2.9.3 | 494 | 9/14/2022 |
1.5.1 | 594 | 7/11/2022 |
1.5.0 | 766 | 7/11/2022 |
1.4.3 | 649 | 7/11/2022 |
1.4.2 | 1,229 | 7/10/2022 |
1.4.1 | 855 | 7/8/2022 |
1.3.10 | 601 | 7/8/2022 |
1.2.9 | 920 | 7/6/2022 |
1.2.8 | 465 | 7/6/2022 |
1.2.7 | 464 | 7/5/2022 |
1.2.6 | 472 | 7/5/2022 |
1.2.5 | 1,589 | 6/23/2022 |
1.2.4 | 457 | 6/22/2022 |
1.2.3 | 705 | 6/21/2022 |
1.2.2 | 495 | 6/14/2022 |
1.2.1 | 2,173 | 6/14/2022 |
1.2.0 | 1,363 | 6/13/2022 |
1.1.1 | 1,858 | 3/16/2022 |
1.1.0 | 486 | 3/9/2022 |
1.0.3 | 619 | 3/4/2022 |
1.0.2 | 480 | 3/3/2022 |
1.0.1 | 479 | 3/3/2022 |
0.9.6 | 489 | 2/20/2022 |
Fixed implicit conversion.