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.
- Intuitive API with discoverability through IntelliSense. All enums can be accessed via the
Enumsclass. - High-performance
- Zero allocations whenever possible.
GetMemberNames(),GetMemberValues()etc. are cached by default. UseDisableCacheto disable it.MemberCountandIsFlagEnumare constants, allowing the compiler to fold them.
- Support for names and descriptions from DisplayAttribute.
- Support for flag enums, including composite values, negative values, duplicate aliases, transformed names and per-member omissions.
- Support for preset, regex, case-pattern and per-member name transformations, plus independent metadata sorting.
- Support for fully or selectively skipping enum values with
[EnumOmitValue]. - Support for public, internal, and accessible protected internal enums, including empty enums and enums in the global namespace or non-generic containing types.
- Support for every C# enum underlying type, explicit/negative values and duplicate values.
- Support for string and span parsing by name, value, display name or description with configurable
StringComparison. - Support for duplicate enum names in different namespaces and escaped C# identifiers.
- Options for controlling namespaces, class names, and other generated-code details. See the Options section below.
Let's create a simple enum and add the [FastEnum] attribute to it.
[FastEnum]
public enum Color
{
Red,
Green,
Blue
}Extensions tell you something about an enum value. For example, MyEnum.Value1.GetString() is equivalent to MyEnum.Value1.ToString() from .NET, but does not need to discover the name at runtime.
The following extensions are auto-generated:
Color color = Color.Red;
Console.WriteLine("String value: " + color.GetString());
Console.WriteLine("Underlying value: " + color.GetUnderlyingValue());Output:
String value: Red
Underlying value: 0
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 lists 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
| API style | Examples | Purpose |
|---|---|---|
| Enum extensions | GetString(), GetUnderlyingValue(), IsFlagSet() |
Operate on a specific enum value. |
| Metadata helper | Enums.Color.TryParse(), GetMemberNames(), IsDefined() |
Parse values and inspect the enum type. |
MemberCount and IsFlagEnum describe the enum. GetMemberNames(), GetMemberValues(), and GetUnderlyingValues() return its included members.
GetString(ColorFormat) selects Name, invariant numeric Value, or available DisplayName/Description metadata. Formats can be combined; Default is Name | Value. Formatting prefers display name, description, name, then value, and falls back to Enum.ToString() when none matches. None uses that fallback directly.
If you add DisplayAttribute to an enum member, the source generator generates display-name and description APIs:
[FastEnum]
internal enum MyEnum
{
[Display(Name = "Value1Name", Description = "Value1Description")]
Value1 = 1,
Value2 = 2
}FastEnum generates GetDisplayName() and GetDescription() extensions for the enum.
MyEnum e = MyEnum.Value1;
Console.WriteLine("Display name: " + e.GetDisplayName());
Console.WriteLine("Description: " + e.GetDescription());Prefer TryGetDisplayName(), TryGetDescription(), and TryGetUnderlyingValue() when you want a boolean plus out pattern instead of exceptions. Enums.MyEnum.GetDisplayNames() and GetDescriptions() return all included metadata pairs.
Output:
Display name: Value1Name
Description: Value1Description
For an enum with FlagsAttribute, FastEnum adds IsFlagSet() and recognizes valid composite values in IsDefined(), TryGetUnderlyingValue(), and GetUnderlyingValue().
[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));
Console.WriteLine("Composite value: " + e.GetUnderlyingValue());Output:
Is Value2 set: False
Composite value: 5
[FastEnum] has several options that control the generated code.
The generated extension class is partial. Set this to the name of your own partial extension class to combine generated and user-authored methods. The default is <EnumName>Extensions.
Multiple enums can share an extension class when their effective extension-class visibility matches. Shared Enums wrappers become public if any generated helper requires it.
Controls the namespace containing the extension class. The default is the enum's namespace.
Use this to override the visibility of the generated extension class. It defaults to the enum's effective visibility, including its containing types (Visibility.Inherit).
[FastEnum(ExtensionClassVisibility = Visibility.Internal)] // Generates an internal StatusExtensions class instead of public.
public enum Status { Ok, Error }Changes the name of the outer Enums wrapper.
Controls the namespace containing the generated metadata helper and format enum. The default is the enum's namespace.
Use this to override the visibility of the generated Enums wrapper class. It defaults to the enum's effective visibility, including its containing types (Visibility.Inherit).
[FastEnum(EnumsClassVisibility = Visibility.Internal)] // Enums.Status will be internal.
public enum Status { Ok, Error }Overrides the generated helper name and the default format-enum and extension-class names. This is useful when generated namespaces bring otherwise distinct enums into the same scope. For example, if your enum is named MyEnum, the generated helper can be accessed like this:
Enums.MyEnum.GetMemberNames()If you set EnumNameOverride to OtherEnum, it will look like this instead:
Enums.OtherEnum.GetMemberNames()Removes the outer static Enums wrapper, changing Enums.MyEnum to MyEnum. Use EnumNameOverride or a different EnumsClassNamespace if the helper would otherwise collide with the enum type.
By default, arrays returned by metadata methods are cached to avoid repeat allocations. Set this option to return a new array on every call instead.
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)] // Uppercase all enum values
[EnumTransform(Regex = "/^Enum//")] // Replace a leading "Enum" with nothing
[EnumTransform(CasePattern = "U_U_U")] // Uppercase the first, third, and fifth charactersYou can specify only one [EnumTransform] per enum.
Regex must have the format /regex-here/replacement-here/.
CasePattern can uppercase, lowercase, or omit characters.
The language uses the following modifier characters:
- U: Uppercase the character.
- L: Lowercase the character.
- O: Omit the character.
- _: Keep the character 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:
Presetuppercases or lowercases all member names.Regexallows replacing a pattern.CasePatternapplies a simple U/L/O/_ mask.SortMemberNames,SortMemberValues,SortUnderlyingValues,SortDisplayNames, andSortDescriptionscontrol the corresponding generated arrays. Each acceptsEnumOrder.None(declaration order),Ascending, orDescending.
[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 = "U____")]
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:
ValueOverridechanges the generated string for that member and whatTryParsewill 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) => trueEnum members can be omitted from all generated APIs or from selected APIs. This is useful when an enum populates a UI list but some values should not be shown.
[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:
Green
[EnumOmitValue] options:
Excludeis a flag enum controlling which generated APIs omit the member. Defaults toEnumOmitExclude.Allwhen not specified.
Targets are GetMemberNames, GetMemberValues, GetUnderlyingValues, TryGetUnderlyingValue, TryParse, TryGetDisplayName, TryGetDescription, IsDefined, and GetString; combine them with |, or use All/None.
[FastEnum]
public enum Color
{
[EnumOmitValue] // Omitted everywhere
Unknown,
[EnumOmitValue(Exclude = EnumOmitExclude.GetMemberNames | EnumOmitExclude.TryParse)]
Red, // Shown in values but hidden from names and parsing
Green
}
// Enums.Color.GetMemberNames() => ["Green"]
// Enums.Color.TryParse("Red", out _) => false
// Enums.Color.GetMemberValues() => [Color.Red, Color.Green]Only GetMemberNames() and TryParse() exclude Red, so it remains available through GetMemberValues().
foreach (Color value in Enums.Color.GetMemberValues())
{
Console.WriteLine(value.ToString());
}Output:
Red
Green
- Enums must be
publicorinternal; private and protected nested enums are not supported, and containing types cannot be less visible than the enum. - Enums inside generic containing types are not supported.
- An enum can have only one
[EnumTransform]attribute.
FastEnum has some additional features compared to .NET's Enum.Parse<T>() and Enum.TryParse<T>():
- Supports StringComparison, defaulting to ordinal comparison.
- Supports parsing
ValueOverridewhen using[EnumTransformValue], plusDisplayNameandDescriptionfrom DisplayAttribute. - Allows
Name,Value,DisplayName, andDescriptionparsing to be selected with a format enum:Enums.MyEnum.TryParse("val", out MyEnum v, MyEnumFormat.Name | MyEnumFormat.DisplayName). - Overloads accept both
stringandReadOnlySpan<char>to avoid unnecessary allocations when parsing substrings.
The IsDefined method differs from the one provided by .NET and supports flags. Enums.MyEnum.IsDefined((MyEnum)42)
and Enums.MyEnum.IsDefined(MyEnum.Value1 | MyEnum.Value3) both work.
Here are benchmarks for calling different methods in .NET versus using FastEnum or Enums.NET. Enums.NET is a high-performance library for working with enum values.
Results were produced with BenchmarkDotNet 0.15.8 on .NET 10.0.11 using an Intel Core i7-12700K. For measurements distinguishable from empty-method overhead, FastEnum is about 9-1,200x faster than the corresponding .NET or reflection APIs and 1.2-7.2x faster than Enums.NET. Measurements close to zero may be indistinguishable from the empty-method overhead.
| Method | Mean | Error | StdDev | Median |
|---|---|---|---|---|
| EnumHasFlag | 0.0028 ns | 0.0019 ns | 0.0017 ns | 0.0028 ns |
| FastEnumHasFlag | 0.0033 ns | 0.0028 ns | 0.0023 ns | 0.0038 ns |
| EnumsNetHasFlag | 0.0015 ns | 0.0049 ns | 0.0044 ns | 0.0000 ns |
| EnumIsDefined | 11.3809 ns | 0.2514 ns | 0.5249 ns | 11.2634 ns |
| FastEnumIsDefined | 0.0000 ns | 0.0000 ns | 0.0000 ns | 0.0000 ns |
| EnumsNetIsDefined | 0.1360 ns | 0.0188 ns | 0.0157 ns | 0.1362 ns |
| EnumIsDefinedFlags | 10.4597 ns | 0.2145 ns | 0.3868 ns | 10.3931 ns |
| FastEnumIsDefinedFlags | 0.0335 ns | 0.0194 ns | 0.0172 ns | 0.0371 ns |
| EnumsNetIsDefinedFlags | 0.0265 ns | 0.0245 ns | 0.0318 ns | 0.0089 ns |
| EnumLength | 9.1790 ns | 0.2088 ns | 0.4670 ns | 9.0306 ns |
| FastEnumLength | 0.0086 ns | 0.0048 ns | 0.0040 ns | 0.0074 ns |
| EnumsNetLength | 1.3818 ns | 0.0131 ns | 0.0116 ns | 1.3847 ns |
| EnumGetNames | 11.4278 ns | 0.2603 ns | 0.3196 ns | 11.3700 ns |
| FastEnumGetNames | 0.5697 ns | 0.0296 ns | 0.0262 ns | 0.5662 ns |
| EnumsNetGetNames | 0.8771 ns | 0.0422 ns | 0.0374 ns | 0.8855 ns |
| EnumToString | 6.2842 ns | 0.1541 ns | 0.2444 ns | 6.1861 ns |
| FastEnumToString | 0.4599 ns | 0.0259 ns | 0.0242 ns | 0.4596 ns |
| EnumsNetToString | 0.9274 ns | 0.0604 ns | 0.0993 ns | 0.9182 ns |
| ReflectionGetDisplayName | 508.2034 ns | 9.7851 ns | 24.7281 ns | 497.2392 ns |
| FastEnumGetDisplayName | 0.4649 ns | 0.0207 ns | 0.0173 ns | 0.4602 ns |
| EnumsNetGetDisplayName | 3.1151 ns | 0.0628 ns | 0.0524 ns | 3.1055 ns |
| EnumTryParse | 11.2563 ns | 0.2212 ns | 0.1847 ns | 11.2343 ns |
| FastEnumTryParse | 0.0000 ns | 0.0000 ns | 0.0000 ns | 0.0000 ns |
| EnumsNetTryParse | 5.2431 ns | 0.1274 ns | 0.1516 ns | 5.2066 ns |
| ReflectionTryParseDisplayName | 756.3817 ns | 14.5033 ns | 39.4573 ns | 741.5761 ns |
| FastEnumTryParseDisplayName | 0.0017 ns | 0.0054 ns | 0.0045 ns | 0.0000 ns |
| EnumsNetTryParseDisplayName | 7.9300 ns | 0.1603 ns | 0.2931 ns | 7.8997 ns |
| EnumGetValues | 0.0278 ns | 0.0157 ns | 0.0139 ns | 0.0252 ns |
| FastEnumGetValues | 0.0047 ns | 0.0100 ns | 0.0094 ns | 0.0000 ns |
| EnumsNetGetValues | 0.0119 ns | 0.0183 ns | 0.0203 ns | 0.0000 ns |
| EnumGetValues | 17.7787 ns | 0.1652 ns | 0.1380 ns | 17.7083 ns |
| FastEnumGetValues | 0.6865 ns | 0.0561 ns | 0.1323 ns | 0.6460 ns |
| EnumsNetGetValues | 0.6081 ns | 0.0390 ns | 0.0365 ns | 0.5991 ns |
| EnumIsDefinedLargeEnum | 18.9906 ns | 0.0684 ns | 0.0534 ns | 18.9888 ns |
| FastEnumIsDefinedLargeEnum | 0.1241 ns | 0.0134 ns | 0.0112 ns | 0.1234 ns |
| EnumsNetIsDefinedLargeEnum | 0.1110 ns | 0.0059 ns | 0.0046 ns | 0.1112 ns |
| EnumLengthLargeEnum | 443.3940 ns | 12.8209 ns | 37.8028 ns | 436.0332 ns |
| FastEnumLengthLargeEnum | 0.0186 ns | 0.0189 ns | 0.0185 ns | 0.0156 ns |
| EnumsNetLengthLargeEnum | 1.1838 ns | 0.0245 ns | 0.0204 ns | 1.1799 ns |
| EnumGetNamesLargeEnum | 431.3348 ns | 8.0160 ns | 11.7497 ns | 427.4168 ns |
| FastEnumGetNamesLargeEnum | 1.1398 ns | 0.0634 ns | 0.0623 ns | 1.1230 ns |
| EnumsNetGetNamesLargeEnum | 0.7173 ns | 0.0558 ns | 0.0902 ns | 0.6992 ns |
| EnumToStringLargeEnum | 19.3085 ns | 0.4218 ns | 0.5484 ns | 19.3017 ns |
| FastEnumToStringLargeEnum | 6.7678 ns | 0.0403 ns | 0.0377 ns | 6.7600 ns |
| EnumsNetToStringLargeEnum | 0.8277 ns | 0.0421 ns | 0.0373 ns | 0.8085 ns |
| FastEnumGetDisplayNameLargeEnum | 0.4668 ns | 0.0297 ns | 0.0278 ns | 0.4561 ns |
| EnumsNetGetDisplayNameLargeEnum | 2.6696 ns | 0.0354 ns | 0.0331 ns | 2.6686 ns |
| ReflectionGetDisplayNameLargeEnum | 549.0822 ns | 10.9259 ns | 19.1358 ns | 540.0147 ns |
| EnumTryParseLargeEnum | 2,468.7719 ns | 25.3011 ns | 23.6666 ns | 2,467.2106 ns |
| FastEnumTryParseLargeEnum | 2.7647 ns | 0.0636 ns | 0.0564 ns | 2.7693 ns |
| EnumsNetTryParseLargeEnum | 6.7580 ns | 0.1547 ns | 0.1519 ns | 6.7152 ns |
| ReflectionTryParseDisplayNameLargeEnum | 192,173.4413 ns | 7,290.0294 ns | 21,033.4032 ns | 182,611.4624 ns |
| FastEnumTryParseDisplayNameLargeEnum | 4.9957 ns | 0.3116 ns | 0.9188 ns | 4.3862 ns |
| EnumsNetTryParseDisplayNameLargeEnum | 11.2140 ns | 0.0777 ns | 0.0606 ns | 11.2271 ns |
| EnumGetValuesLargeEnum | 0.0000 ns | 0.0000 ns | 0.0000 ns | 0.0000 ns |
| FastEnumGetValuesLargeEnum | 5.0796 ns | 0.2452 ns | 0.7231 ns | 5.1458 ns |
| EnumsNetGetValuesLargeEnum | 0.0111 ns | 0.0195 ns | 0.0182 ns | 0.0000 ns |
| EnumGetValuesLargeEnum | 185.1160 ns | 3.6635 ns | 5.9159 ns | 183.9072 ns |
| FastEnumGetValuesLargeEnum | 1.0278 ns | 0.0609 ns | 0.1350 ns | 0.9884 ns |
| EnumsNetGetValuesLargeEnum | 0.9190 ns | 0.0370 ns | 0.0346 ns | 0.9232 ns |