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
Enums
class. - High-performance
- Zero allocations whenever possible.
GetMemberNames()
,GetMemberValues()
etc. are cached by default. UseDisableCache
to disable it.MemberCount
andIsFlagsEnum
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.
Lets create a very simple enum, and add the [FastEnum]
attribute to it.
[FastEnum]
public enum Color
{
Red,
Green,
Blue
}
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
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
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());
Output:
Display name: Value1Name
Description: Value1Description
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
[FastEnum]
have several options to control the behavior of the generated code.
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.
Use this to control which namespace the extensions class belongs to. Defaults to the namespace of the enum.
Use this to set the name of the Enums
class to something else.
Used this to specify the namespace for the Enums class. Defaults to the namespace of the enum.
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()
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.
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.
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.
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
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
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 supportsDisplayName
andDescription
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)
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.
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.
Method | Mean | Error | StdDev | Median |
---|---|---|---|---|
EnumHasFlag | 0.0056 ns | 0.0083 ns | 0.0074 ns | 0.0030 ns |
CodeGenHasFlag | 0.0101 ns | 0.0062 ns | 0.0058 ns | 0.0083 ns |
EnumsNetHasFlag | 0.6253 ns | 0.0238 ns | 0.0211 ns | 0.6192 ns |
EnumIsDefined | 42.6278 ns | 0.4633 ns | 0.4334 ns | 42.5181 ns |
CodeGenIsDefined | 0.0045 ns | 0.0066 ns | 0.0058 ns | 0.0013 ns |
EnumsNetIsDefined | 0.4842 ns | 0.0101 ns | 0.0085 ns | 0.4845 ns |
EnumLength | 20.5705 ns | 0.3509 ns | 0.5566 ns | 20.5117 ns |
CodeGenLength | 0.0001 ns | 0.0004 ns | 0.0003 ns | 0.0000 ns |
EnumsNetLength | 5.3362 ns | 0.1085 ns | 0.0906 ns | 5.3495 ns |
EnumGetNames | 17.5890 ns | 0.2905 ns | 0.2717 ns | 17.6505 ns |
CodeGenGetNames | 1.5764 ns | 0.0476 ns | 0.0446 ns | 1.5843 ns |
EnumsNetGetNames | 1.6052 ns | 0.0323 ns | 0.0286 ns | 1.5994 ns |
EnumToString | 15.3389 ns | 0.1815 ns | 0.1698 ns | 15.3113 ns |
CodeGenToString | 0.8379 ns | 0.0380 ns | 0.0337 ns | 0.8267 ns |
EnumsNetToString | 21.3269 ns | 0.0836 ns | 0.0653 ns | 21.3056 ns |
ReflectionGetDisplayName | 1,008.3327 ns | 6.6111 ns | 5.8606 ns | 1,007.1464 ns |
CodeGenGetDisplayName | 3.2780 ns | 0.1182 ns | 0.1495 ns | 3.2501 ns |
EnumsNetGetDisplayName | 9.3953 ns | 0.1237 ns | 0.0966 ns | 9.4036 ns |
EnumTryParse | 31.4662 ns | 0.4013 ns | 0.3557 ns | 31.3008 ns |
CodeGenTryParse | 6.4197 ns | 0.0495 ns | 0.0463 ns | 6.4246 ns |
EnumsNetTryParse | 41.8299 ns | 0.4277 ns | 0.4001 ns | 41.7159 ns |
ReflectionTryParseDisplayName | 1,763.8422 ns | 17.7619 ns | 15.7455 ns | 1,764.9430 ns |
CodeGenTryParseDisplayName | 3.9371 ns | 0.0177 ns | 0.0138 ns | 3.9381 ns |
EnumsNetTryParseDisplayName | 40.4373 ns | 0.7966 ns | 0.7061 ns | 40.6816 ns |
EnumGetValues | 0.0000 ns | 0.0000 ns | 0.0000 ns | 0.0000 ns |
CodeGenGetValues | 2.2281 ns | 0.0281 ns | 0.0263 ns | 2.2237 ns |
EnumsNetGetValues | 4.6160 ns | 0.0868 ns | 0.0769 ns | 4.6156 ns |
EnumGetValues | 254.3910 ns | 2.2232 ns | 1.8565 ns | 253.8503 ns |
CodeGenGetValues | 1.6647 ns | 0.0508 ns | 0.0475 ns | 1.6750 ns |
EnumsNetGetValues | 2.4889 ns | 0.0416 ns | 0.0389 ns | 2.4874 ns |