FastEnum

December 6, 2025 ยท View on GitHub

NuGet License

Description

A source generator to generate common methods for your enum types at compile-time. Print values, parse, or get the underlying value of enums without using reflection.

Features

  • Intuitive API with discoverability through IntelliSense. All enums can be accessed via the Enums class.
  • High-performance
    • Zero allocations whenever possible.
    • GetMemberNames(), GetMemberValues() etc. are cached by default. Use DisableCache to disable it.
    • MemberCount and IsFlagsEnum is const to allow the compiler to fold constants.
  • Supports name and description from DisplayAttribute.
  • Support for flag enums via the FlagsAttribute.
  • Support for skipping enum values completely with [EnumOmitValue] on enum members.
  • Support for private/internal enums
  • Support for enums nested inside classes
  • Support for user-set underlying values such as long, uint, byte, etc.
  • Support for duplicate enum names from different namespaces
  • Support for enums that reside in the global namespace
  • Has several options to control namespace, class name and more for generated code. See Options section below for details.

Examples

Lets create a very simple enum, and add the [FastEnum] attribute to it.

[FastEnum]
public enum Color
{
    Red,
    Green,
    Blue
}

Extensions

Extensions tell you something about an instance of an enum you have. For example, MyEnum.Value1.GetString() is the same as MyEnum.Value1.ToString() from dotnet, except that it does not need to do any work at runtime.

The following extensions are auto-generated:

Color c = Color.Red;

Console.WriteLine("String value: " + e.GetString());
Console.WriteLine("Underlying value: " + e.GetUnderlyingValue());

Output:

String value: Red
Underlying value: 0

Enums class

Enums is a class that contains metadata about the auto-generated enum.

Console.WriteLine("Number of members: " + Enums.Color.MemberCount);
Console.WriteLine("Parse: " + Enums.Color.Parse("Red"));
Console.WriteLine("Is Green part of the enum: " + Enums.Color.IsDefined(Color.Green));

PrintArray("Member names:", Enums.Color.GetMemberNames());
PrintArray("Underlying values:", Enums.Color.GetUnderlyingValues());

PrintArray simply iterates an array and list the values on separate lines.

Output:

Number of members: 3
Parse: Red
Is Green part of the enum: True
Member names:
- Red
- Green
- Blue
Underlying values:
- 0
- 1
- 2

Values via attributes

DisplayAttribute

If you add DisplayAttribute to an enum, the source generator will generate extra methods. For example, you can add [DisplayAttribute] to an enum value like this:

[FastEnum]
internal enum MyEnum
{
    [Display(Name = "Value1Name", Description = "Value1Description")]
    Value1 = 1,
    Value2 = 2
}

It will generate GetDisplayName() and GetDescription() extensions to your enum.

MyEnum e = MyEnum.Value1;
Console.WriteLine("Display name: " + e.GetDisplayName());
Console.WriteLine("Description: " + e.GetDescription());

Prefer the TryGetDisplayName(), TryGetDescription() and TryGetUnderlyingValue() helpers when you want a boolean + out pattern instead of exceptions.

Output:

Display name: Value1Name
Description: Value1Description

FlagsAttribute

If you have an enum with the FlagsAttribute, FastEnum will add a method called IsFlagSet().

[Flags]
[FastEnum]
internal enum MyFlagsEnum
{
    None = 0,
    Value1 = 1,
    Value2 = 2
    Value3 = 4
}
MyFlagsEnum e = MyFlagsEnum.Value1 | MyFlagsEnum.Value3;
Console.WriteLine("Is Value2 set: " + e.IsFlagSet(MyFlagsEnum.Value2));

Output:

Is Value2 set: False

Options

[FastEnum] have several options to control the behavior of the generated code.

ExtensionClassName

The generated extension class is partial by default. So if you want to combine extension from your own class and the autogenerated one, you can use this option to set the name to the same as your extensions class. Defaults to <EnumName>Extensions.

ExtensionClassNamespace

Use this to control which namespace the extensions class belongs to. Defaults to the namespace of the enum.

ExtensionClassVisibility

Use this to override the visibility of the generated extensions class. Defaults to the enum's own visibility (Visibility.Inherit).

[FastEnum(ExtensionClassVisibility = Visibility.Internal)] // Generates an internal StatusExtensions class instead of public.
public enum Status { Ok, Error }

EnumsClassName

Use this to set the name of the Enums class to something else.

EnumsClassNamespace

Used this to specify the namespace for the Enums class. Defaults to the namespace of the enum.

EnumsClassVisibility

Use this to override the visibility of the generated Enums wrapper class. Defaults to the enum's own visibility (Visibility.Inherit).

[FastEnum(EnumsClassVisibility = Visibility.Internal)] // Enums.Status will be internal.
public enum Status { Ok, Error }

EnumNameOverride

Sometimes you might have two enums named the same, but in different namespaces. You can use this option to override the name of the enum in the generated code. For example, if your enum is named MyEnum the Enums class can be accessed like this:

Enums.MyEnum.GetMemberNames()

If you set EnumNameOverride to OtherEnum it will look like this instead:

Enums.OtherEnum.GetMemberNames()

DisableEnumsWrapper

Enable this to avoid generating the static Enums class that wraps all the enums. When enabled, Enums.MyEnum becomes MyEnum. This is handy if you want to set all enums inside the same namespace across projects. Note that you must use EnumNameOverride or set EnumsClassNamespace to something to avoid the name collision between your enum and the generated class.

DisableCache

By default arrays from GetMemberNames(), GetMemberValues() etc. is cached to avoid an allocation each time you call them. If your application only needs the arrays once, then caching them in memory take up unnecessary space. Use this option to disable the cache.

Transformations

You can transform the string output of enums with [EnumTransform] at compile time. There are a few ways to do this.

[EnumTransform(Preset = EnumTransform.UpperCase)] //Will uppercase all enum values
[EnumTransform(Regex = "/^Enum//")] //A regex to replace values starting with "Enum" with nothing.
[EnumTransform(CasePattern = "U_U_U")] //Uppercase the first, third and fifth characters

Note: You can only specify one [EnumTransform] at the time.

Regex must have the format /regex-here/replacement-here/.

CasePattern is a way to either uppercase, lowercase or omit charaters.

The language uses the following modifier chars:

  • U: Uppercase the char
  • L: Lowercase the char
  • O: Omit the char
  • _: Do nothing. Keep the char as-is.

Let's say you want to omit the first character in all values, uppercase the third character and lowercase the rest.

[FastEnum]
[EnumTransform(CasePattern = "OOULLLLL")]
public enum MyEnum
{
    Myvalue1,
    Myvalue2,
    Myvalue3
}

The pattern is matched as much as possible. A pattern of U will simply uppercase the first character, and a pattern of UUUUUUUUUUUU will uppercase the first 12 characters, even if the enum value is only 6 characters long.

[EnumTransform] options:

  • Preset uppercases or lowercases all member names.
  • Regex allows replacing a pattern.
  • CasePattern applies a simple U/L/O/_ mask.
  • SortMemberNames, SortMemberValues, SortUnderlyingValues, SortDisplayNames, SortDescriptions control ordering of the corresponding generated arrays. Defaults to EnumOrder.None; set to Ascending or Descending to change.
[FastEnum]
[EnumTransform(Preset = EnumTransform.UpperCase)]
public enum Color { Red, Green }
// GetString(Color.Red) => "RED"

[FastEnum]
[EnumTransform(Regex = "/^Clr//")]
public enum Color { ClrRed, ClrGreen }
// GetString(Color.ClrRed) => "Red"

[FastEnum]
[EnumTransform(CasePattern = "UOlll")]
public enum Color { apple, pears }
// GetString(Color.apple) => "Apple"
// GetString(Color.pears) => "Pears"

[FastEnum]
[EnumTransform(SortMemberNames = EnumOrder.Descending)]
public enum Nato { Alpha, Bravo, Charlie }
// GetMemberNames() => ["Charlie", "Bravo", "Alpha"]

You can override the string for specific members with [EnumTransformValue(ValueOverride = "...")]. This is useful when most values follow a pattern but a few need custom text.

[EnumTransformValue] options:

  • ValueOverride changes the generated string for that member and what TryParse will accept for it.
[FastEnum]
public enum Status
{
    [EnumTransformValue(ValueOverride = "all good")]
    Ok,
    Error
}
// GetString(Status.Ok) => "all good"
// Enums.Status.TryParse("all good", out var s) => true

[EnumTransform] also accepts ordering hints so generated lists come out sorted without extra runtime work:

  • SortMemberNames, SortMemberValues, SortUnderlyingValues affect the ordering of GetMemberNames(), GetMemberValues() and GetUnderlyingValues().
  • SortDisplayNames, SortDescriptions affect the ordering of GetDisplayNames() and GetDescriptions().
  • Values can be EnumOrder.None (default; retain declared order), EnumOrder.Ascending, or EnumOrder.Descending.

Omitting values

It is possible to omit enum value both fully and partially. This is useful if you, let's say, want to use the enum values directly in a dropdown on a website, but don't want to include a value in the list.

[FastEnum]
public enum Color
{
    [EnumOmitValue] //Completely omitted
    Unknown,

    [EnumOmitValue(Exclude = EnumOmitExclude.GetMemberNames] //Partially omitted
    Red,
    Green
}

If you call GetMemberNames() or any other method on the Enums.Color class, the Unknown value will be omitted.

foreach (string name in Enums.Color.GetMemberNames())
{
    Console.WriteLine(name);
}

Output:

Red
Green

[EnumOmitValue] options:

  • Exclude is a flag enum controlling which generated APIs omit the member. Defaults to EnumOmitExclude.All when not specified.
[FastEnum]
public enum Color
{
    [EnumOmitValue] //omitted everywhere
    Unknown,

    [EnumOmitValue(Exclude = EnumOmitExclude.GetMemberNames | EnumOmitExclude.TryParse)]
    Red, //shown in values but hidden from names/parse
    Green
}

// Enums.Color.GetMemberNames() => ["Green"]
// Enums.Color.TryParse("Red", out _) => false
// Enums.Color.GetMemberValues() => [Color.Red, Color.Green]

Since we excluded GetMemberNames() for the Red color, it showed up in the list above, but it won't show up when calling GetMemberValues().

foreach (Color value in Enums.Color.GetMemberValues())
{
    Console.WriteLine(value.ToString());
}

Output:

Green

Notes

Parse/TryParse methods

FastEnum has some additional features compared to dotnet's Enum.Parse<T>() and Enum.TryParse<T>():

  • Supports StringComparison (defaults to ordinal comparison)
  • Supports parsing ValueOverride when using [EnumTransformValue]. Also supports DisplayName and Description when using DisplayAttribute
  • You can enable/disable Name, Value, DisplayName or Description parsing via an enum: Enums.MyEnum.TryParse("val", out MyEnum v, MyEnumFormat.Name | MyEnumFormat.DisplayName)
  • Overloads accept both string and ReadOnlySpan<char> to avoid unnecessary allocations when parsing substrings.

IsDefined method

The IsDefined method is different than the one provided by dotnet. It supports flags out of the box. Enums.MyEnum.IsDefined((MyEnum)42) and Enums.MyEnum.IsDefined(MyEnum.Value1 | MyEnum.Value3) both work.

Benchmarks

Here are benchmarks for calling different methods in dotnet vs. using CodeGen vs. using Enums.net. Enums.net is a high-performance library for working with enum values.

The table below shows that Enums.net is between 2-80x faster than dotnet, but FastEnum is 2-14x faster than Enums.net.

MethodMeanErrorStdDevMedian
EnumHasFlag0.0056 ns0.0083 ns0.0074 ns0.0030 ns
CodeGenHasFlag0.0101 ns0.0062 ns0.0058 ns0.0083 ns
EnumsNetHasFlag0.6253 ns0.0238 ns0.0211 ns0.6192 ns
EnumIsDefined42.6278 ns0.4633 ns0.4334 ns42.5181 ns
CodeGenIsDefined0.0045 ns0.0066 ns0.0058 ns0.0013 ns
EnumsNetIsDefined0.4842 ns0.0101 ns0.0085 ns0.4845 ns
EnumLength20.5705 ns0.3509 ns0.5566 ns20.5117 ns
CodeGenLength0.0001 ns0.0004 ns0.0003 ns0.0000 ns
EnumsNetLength5.3362 ns0.1085 ns0.0906 ns5.3495 ns
EnumGetNames17.5890 ns0.2905 ns0.2717 ns17.6505 ns
CodeGenGetNames1.5764 ns0.0476 ns0.0446 ns1.5843 ns
EnumsNetGetNames1.6052 ns0.0323 ns0.0286 ns1.5994 ns
EnumToString15.3389 ns0.1815 ns0.1698 ns15.3113 ns
CodeGenToString0.8379 ns0.0380 ns0.0337 ns0.8267 ns
EnumsNetToString21.3269 ns0.0836 ns0.0653 ns21.3056 ns
ReflectionGetDisplayName1,008.3327 ns6.6111 ns5.8606 ns1,007.1464 ns
CodeGenGetDisplayName3.2780 ns0.1182 ns0.1495 ns3.2501 ns
EnumsNetGetDisplayName9.3953 ns0.1237 ns0.0966 ns9.4036 ns
EnumTryParse31.4662 ns0.4013 ns0.3557 ns31.3008 ns
CodeGenTryParse6.4197 ns0.0495 ns0.0463 ns6.4246 ns
EnumsNetTryParse41.8299 ns0.4277 ns0.4001 ns41.7159 ns
ReflectionTryParseDisplayName1,763.8422 ns17.7619 ns15.7455 ns1,764.9430 ns
CodeGenTryParseDisplayName3.9371 ns0.0177 ns0.0138 ns3.9381 ns
EnumsNetTryParseDisplayName40.4373 ns0.7966 ns0.7061 ns40.6816 ns
EnumGetValues0.0000 ns0.0000 ns0.0000 ns0.0000 ns
CodeGenGetValues2.2281 ns0.0281 ns0.0263 ns2.2237 ns
EnumsNetGetValues4.6160 ns0.0868 ns0.0769 ns4.6156 ns
EnumGetValues254.3910 ns2.2232 ns1.8565 ns253.8503 ns
CodeGenGetValues1.6647 ns0.0508 ns0.0475 ns1.6750 ns
EnumsNetGetValues2.4889 ns0.0416 ns0.0389 ns2.4874 ns