Skip to main content

RapidEnum by hanachiru

NuGet / site data

Nuget GitHub last commit GitHub Repo stars

Details

Info

info

Name: RapidEnum

Enum utility with SourceGenerator for C#/.NET

Author: hanachiru

NuGet: https://www.nuget.org/packages/RapidEnum/

You can find more details at https://github.com/hanachiru/RapidEnum

Source: https://github.com/hanachiru/RapidEnum

Author

note

hanachiru Alt text

Original Readme

note

RapidEnum

nuget Releases license test

日本語版

RapidEnum is a Source Generator that provides fast-running enum utilities for C#/.NET. It is faster than the .NET API and achieves zero allocation for all methods.

Package - RapidEnum

PerformanceComparison

It performed better than .NET API. It is also faster than FastEnum v1.8.0. For more information on performance comparisons, see this.

RapidEnum is heavily influenced by FastEnum. API is very similar to FastEnum. Thanks to xin9le for creating a great library!

Table of Contents

Requirements

  • .NET Standard2.0 or newer
  • Unity 2022.3.12f1 or newer

Installation

NuGet

$ dotnet add package RapidEnum

nuget.org : RapidEnum

Unity

Add the following git URL from the Package Manager

https://github.com/hanachiru/RapidEnum.git?path=/RapidEnum.Unity/Packages/com.hanachiru.rapidenum

UPM

asmdef settings

If you have created a .asmdef, you need to add RapidEnum to the Assembly Definition References.

How to use

Basic usage

Attaching [RapidEnum] to the target enum generates an enum utility class. Note that this is only valid for public or internal enum.

[RapidEnum]
public enum Weather
{
Sun,
Cloud,
Rain,
Snow
}

The Enum name + EnumExtensions class defines the relevant methods.

// Sun,Cloud,Rain,Snow
IReadOnlyList<Weather> values = WeatherEnumExtensions.GetValues();

// Sun,Cloud,Rain,Snow
IReadOnlyList<string> names = WeatherEnumExtensions.GetNames();

// Rain
string name = WeatherEnumExtensions.GetName(Weather.Rain);

// Cloud
string str = Weather.Cloud.ToStringFast();

// True
bool defined = WeatherEnumExtensions.IsDefined("Sun");

// Sun
Weather parse = WeatherEnumExtensions.Parse("Sun");

// True
// Sun
bool tryParse = WeatherEnumExtensions.TryParse("Sun", out Weather value);

How to use it for any enum

The [RapidEnumWithType] can be used to generate utility classes for any enum.

For static partial class that are public or internal, give them a [RapidEnumWithType] with the target enum as an argument. The class name can be any string, but Enum name + EnumExtensions is easier to understand.

// System.DateTimeKind has Unspecified, Utc, Local
[RapidEnumWithType(typeof(DateTimeKind))]
public static partial class DateTimeKindEnumExtensions
{
}

There is no performance difference compared to using [RapidEnum]. Use [RapidEnumWithType] if [RapidEnum] cannot be given, such as an enum provided by a third-party library.

// Unspecified,Utc,Local
IReadOnlyList<DateTimeKind> values = DateTimeKindEnumExtensions.GetValues();

// Unspecified,Utc,Local
IReadOnlyList<string> names = DateTimeKindEnumExtensions.GetNames();

// Local
string name = DateTimeKindEnumExtensions.GetName(DateTimeKind.Local);

// Local
string str = DateTimeKind.Local.ToStringFast();

// True
bool defined = DateTimeKindEnumExtensions.IsDefined("Local");

// Local
DateTimeKind parse = DateTimeKindEnumExtensions.Parse("Local");

// True
// Local
bool tryParse = DateTimeKindEnumExtensions.TryParse("Local", out DateTimeKind value);

Get Name and Value as a pair

Use the GetMembers and GetMember methods if you want to get the Name and Value of enum in pairs.

WeatherEnumExtensions.Member member = WeatherEnumExtensions.GetMember(Weather.Rain);
var (name, value) = member;

foreach (WeatherEnumExtensions.Member item in WeatherEnumExtensions.GetMembers())
{
Console.WriteLine($"Name : {item.Name}, Value : {item.Value}");
}

Use EnumMemberAttribute

If you use EnumMemberAttribute, you can get the Value property of EnumMemberAttribute.

[RapidEnum]
public enum Weather
{
[EnumMember(Value = "sun")]
Sun,
[EnumMember]
Cloud,
[EnumMember(Value = "rain")]
Rain,
Snow
}
// sun
string enumMemberValue = Weather.Sun.GetEnumMemberValue();

// null
string enumMemberValue = Weather.Cloud.GetEnumMemberValue();

Performance comparison

MethodMeanErrorStdDevMedianGen0Allocated
RapidEnum_GetValues0.0042 ns0.0059 ns0.0052 ns0.0028 ns--
FastEnum_GetValues0.0083 ns0.0086 ns0.0081 ns0.0055 ns--
NET_GetValues64.4620 ns0.9908 ns0.9268 ns64.2767 ns0.004840 B
RapidEnum_GetNames0.0006 ns0.0017 ns0.0015 ns0.0000 ns--
FastEnum_GetNames0.0025 ns0.0031 ns0.0028 ns0.0012 ns--
NET_GetNames12.3820 ns0.1086 ns0.1016 ns12.4076 ns0.006756 B
RapidEnum_GetName0.0069 ns0.0085 ns0.0071 ns0.0039 ns--
FastEnum_GetName0.2530 ns0.0070 ns0.0065 ns0.2527 ns--
NET_GetName15.9190 ns0.0524 ns0.0490 ns15.9046 ns0.002924 B
RapidEnum_ToString0.0103 ns0.0049 ns0.0046 ns0.0110 ns--
FastEnum_ToString0.4844 ns0.0062 ns0.0052 ns0.4845 ns--
NET_ToString6.1700 ns0.0451 ns0.0376 ns6.1493 ns0.002924 B
RapidEnum_IsDefines0.0026 ns0.0036 ns0.0034 ns0.0000 ns--
FastEnum_IsDefines4.6724 ns0.0583 ns0.0545 ns4.6434 ns--
NET_IsDefines14.5923 ns0.0355 ns0.0332 ns14.5996 ns--
RapidEnum_Parse0.9258 ns0.0161 ns0.0150 ns0.9240 ns--
FastEnum_Parse4.6223 ns0.0082 ns0.0073 ns4.6192 ns--
NET_Parse8.8707 ns0.0965 ns0.0903 ns8.8293 ns--
RapidEnum_TryParse0.7633 ns0.0097 ns0.0090 ns0.7657 ns--
FastEnum_TryParse4.6869 ns0.0254 ns0.0212 ns4.6852 ns--
NET_TryParse8.8433 ns0.0609 ns0.0569 ns8.8268 ns--

PerformanceComparison

Benchmark Source

BenchmarkDotNet v0.14.0, macOS Sonoma 14.4.1 (23E224) [Darwin 23.4.0]
Apple M2 Pro, 1 CPU, 12 logical and 12 physical cores
.NET SDK 8.0.303
[Host] : .NET 8.0.7 (8.0.724.31311), Arm64 RyuJIT AdvSIMD
DefaultJob : .NET 8.0.7 (8.0.724.31311), Arm64 RyuJIT AdvSIMD

About

note

Generate enum values without reflection

How to use

Example (source csproj, source files)

This is the CSharp Project that references RapidEnum

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

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<PropertyGroup>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="RapidEnum" Version="1.0.2" />
</ItemGroup>

</Project>

Generated Files

Those are taken from $(BaseIntermediateOutputPath)\GX

// <auto-generated />

namespace EnumDemo
{

public static partial class CarTypesEnumExtensions
{
[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
public static string ToStringFast(this global::EnumDemo.CarTypes value)
{
return value switch
{
global::EnumDemo.CarTypes.None => nameof(global::EnumDemo.CarTypes.None),
global::EnumDemo.CarTypes.Dacia => nameof(global::EnumDemo.CarTypes.Dacia),
global::EnumDemo.CarTypes.Tesla => nameof(global::EnumDemo.CarTypes.Tesla),
global::EnumDemo.CarTypes.BMW => nameof(global::EnumDemo.CarTypes.BMW),
global::EnumDemo.CarTypes.Mercedes => nameof(global::EnumDemo.CarTypes.Mercedes),
_ => value.ToString()
};
}

[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
public static bool IsDefined(global::EnumDemo.CarTypes value)
{
return value switch
{
global::EnumDemo.CarTypes.None => true,
global::EnumDemo.CarTypes.Dacia => true,
global::EnumDemo.CarTypes.Tesla => true,
global::EnumDemo.CarTypes.BMW => true,
global::EnumDemo.CarTypes.Mercedes => true,
_ => false,
};
}

[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
public static bool IsDefined(string name)
{
return name switch
{
nameof(global::EnumDemo.CarTypes.None) => true,
nameof(global::EnumDemo.CarTypes.Dacia) => true,
nameof(global::EnumDemo.CarTypes.Tesla) => true,
nameof(global::EnumDemo.CarTypes.BMW) => true,
nameof(global::EnumDemo.CarTypes.Mercedes) => true,
_ => false,
};
}

private static readonly global::System.Collections.ObjectModel.ReadOnlyCollection<global::EnumDemo.CarTypes> CacheValues = new global::System.Collections.ObjectModel.ReadOnlyCollection<global::EnumDemo.CarTypes>(new[]
{
global::EnumDemo.CarTypes.None,
global::EnumDemo.CarTypes.Dacia,
global::EnumDemo.CarTypes.Tesla,
global::EnumDemo.CarTypes.BMW,
global::EnumDemo.CarTypes.Mercedes,
});

[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
public static global::System.Collections.Generic.IReadOnlyList<global::EnumDemo.CarTypes> GetValues() => CacheValues;

private static readonly global::System.Collections.ObjectModel.ReadOnlyCollection<string> CacheNames = new global::System.Collections.ObjectModel.ReadOnlyCollection<string>(new[]
{
nameof(global::EnumDemo.CarTypes.None),
nameof(global::EnumDemo.CarTypes.Dacia),
nameof(global::EnumDemo.CarTypes.Tesla),
nameof(global::EnumDemo.CarTypes.BMW),
nameof(global::EnumDemo.CarTypes.Mercedes),
});

[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
public static global::System.Collections.Generic.IReadOnlyList<string> GetNames() => CacheNames;

[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
public static string GetName(global::EnumDemo.CarTypes value)
{
return value.ToStringFast();
}

private static readonly global::System.Collections.ObjectModel.ReadOnlyCollection<Member> CacheMembers = new global::System.Collections.ObjectModel.ReadOnlyCollection<Member>(new[]
{
new Member(nameof(global::EnumDemo.CarTypes.None), global::EnumDemo.CarTypes.None),
new Member(nameof(global::EnumDemo.CarTypes.Dacia), global::EnumDemo.CarTypes.Dacia),
new Member(nameof(global::EnumDemo.CarTypes.Tesla), global::EnumDemo.CarTypes.Tesla),
new Member(nameof(global::EnumDemo.CarTypes.BMW), global::EnumDemo.CarTypes.BMW),
new Member(nameof(global::EnumDemo.CarTypes.Mercedes), global::EnumDemo.CarTypes.Mercedes),
});

[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
public static global::System.Collections.Generic.IReadOnlyList<Member> GetMembers() => CacheMembers;

[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
public static Member GetMember(global::EnumDemo.CarTypes value)
{
return value.ToMember();
}

[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
public static Member ToMember(this global::EnumDemo.CarTypes value)
{
return new Member(ToStringFast(value), value);
}

public sealed class Member
{
public string Name \{ get; }
public global::EnumDemo.CarTypes Value \{ get; }

internal Member(string name, global::EnumDemo.CarTypes value)
{
Name = name;
Value = value;
}

public void Deconstruct(out string name, out global::EnumDemo.CarTypes value)
{
name = Name;
value = Value;
}
}

[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
public static global::EnumDemo.CarTypes Parse(string name, bool ignoreCase = false)
{
if (TryParse(name, out var value, ignoreCase))
{
return value;
}
throw new global::System.ArgumentException($"The value '{name}' is not defined in enum 'global::EnumDemo.CarTypes'.");
}

[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
public static bool TryParse(
string name,
out global::EnumDemo.CarTypes value,
bool ignoreCase = false)
{
return ignoreCase ? TryParseIgnoreCase(name, out value) : TryParse(name, out value);
}

private static bool TryParseIgnoreCase(
string name,
out global::EnumDemo.CarTypes value)
{
switch (name)
{
case not null when name.Equals(nameof(global::EnumDemo.CarTypes.None), global::System.StringComparison.OrdinalIgnoreCase):
value = global::EnumDemo.CarTypes.None;
return true;
case not null when name.Equals(nameof(global::EnumDemo.CarTypes.Dacia), global::System.StringComparison.OrdinalIgnoreCase):
value = global::EnumDemo.CarTypes.Dacia;
return true;
case not null when name.Equals(nameof(global::EnumDemo.CarTypes.Tesla), global::System.StringComparison.OrdinalIgnoreCase):
value = global::EnumDemo.CarTypes.Tesla;
return true;
case not null when name.Equals(nameof(global::EnumDemo.CarTypes.BMW), global::System.StringComparison.OrdinalIgnoreCase):
value = global::EnumDemo.CarTypes.BMW;
return true;
case not null when name.Equals(nameof(global::EnumDemo.CarTypes.Mercedes), global::System.StringComparison.OrdinalIgnoreCase):
value = global::EnumDemo.CarTypes.Mercedes;
return true;
case not null when int.TryParse(name, out var val):
value = (global::EnumDemo.CarTypes)val;
return true;
default:
value = default;
return false;
}
}

private static bool TryParse(
string name,
out global::EnumDemo.CarTypes value)
{
switch (name)
{
case nameof(global::EnumDemo.CarTypes.None):
value = global::EnumDemo.CarTypes.None;
return true;
case nameof(global::EnumDemo.CarTypes.Dacia):
value = global::EnumDemo.CarTypes.Dacia;
return true;
case nameof(global::EnumDemo.CarTypes.Tesla):
value = global::EnumDemo.CarTypes.Tesla;
return true;
case nameof(global::EnumDemo.CarTypes.BMW):
value = global::EnumDemo.CarTypes.BMW;
return true;
case nameof(global::EnumDemo.CarTypes.Mercedes):
value = global::EnumDemo.CarTypes.Mercedes;
return true;
case not null when int.TryParse(name, out var val):
value = (global::EnumDemo.CarTypes)val;
return true;
default:
value = default;
return false;
}
}

private static readonly global::System.Type CacheUnderlyingType = global::System.Enum.GetUnderlyingType(typeof(global::EnumDemo.CarTypes));
public static global::System.Type GetUnderlyingType() => CacheUnderlyingType;

[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
public static string GetEnumMemberValue(this global::EnumDemo.CarTypes value)
{
return value switch
{
global::EnumDemo.CarTypes.None => null,
global::EnumDemo.CarTypes.Dacia => null,
global::EnumDemo.CarTypes.Tesla => null,
global::EnumDemo.CarTypes.BMW => null,
global::EnumDemo.CarTypes.Mercedes => null,
_ => null
};
}
}
}

Useful

Download Example (.NET C#)

Share RapidEnum

https://ignatandrei.github.io/RSCG_Examples/v2/docs/RapidEnum

Category "Enum" has the following generators:

1 CredFetoEnum

2 EnumClass

3 EnumsEnhanced

4 EnumUtilities

5 Flaggen

6 FusionReactor

7 Genbox.FastEnum

8 jos.enumeration

9 NetEscapades.EnumGenerators

10 PMart.Enumeration

11 RapidEnum

12 requiredenum

See category

Enum