EnumsEnhanced is a C# source generator for generating common (extension) methods for enums without having the cost of the Reflection API (slow and many allocations).
Following extension methods will be generated for each enum:
| Default | Equivalent |
|---|---|
| GetName | GetNameFast |
| GetNames | GetNamesFast |
| GetValues | GetValuesFast |
| HasFlag | HasFlagFast, HasFlagUnsafe |
| IsDefined | IsDefinedFast |
| Parse | ParseFast |
| TryParse | TryParseFast |
| ToString | ToStringFast |
Also it will only generate the extension methods if the enum has no containing type.
internal class EnumContainingTypeTest
{
// Will not generate any code for this enum.
public enum ContainingTypeEnum
{
Test1
}
}The generated class will always have the same naming scheme by using your enum type name with an Enhanced postfix (e.g. YourEnumNameEnhanced)
or in this example TestEnumEnhanced.
namespace EnumsEnhancedTest;
[Flags]
internal enum TestEnum
{
Test1 = 1 << 0,
Test_1 = 1 << 0,
Test2 = 1 << 1,
Test4 = 1 << 2,
Test5 = 1 << 4,
Test6 = 1 << 6,
Test7
}Generated code:
#nullable enable
using System.Text;
using System.Runtime.CompilerServices;
using System.Collections.Generic;
using System;
using System;
using System.Globalization;
namespace EnumsEnhancedTest {
/// <summary>
/// Reflection free extension methods for the <see cref="TestEnum"/> type.
/// </summary>
internal static partial class TestEnumEnhanced {
/// <summary>
/// Determines whether one or more bit fields are set in the current instance.
/// </summary>
/// <param name="e">The value of the enum.</param>
/// <param name="flag">The flag to check.</param>
/// <returns><see langword="true"/> if the bit field or bit fields that are set in flag are also set in the current instance; otherwise, false.</returns>
#if NETCOREAPP3_0_OR_GREATER
#else
[MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
#endif
public static bool HasFlagFast(this TestEnum e, TestEnum flag) {
#if NETCOREAPP3_0_OR_GREATER
Int32 flagsValue = Unsafe.As < TestEnum, Int32 > (ref flag);
return (Unsafe.As < TestEnum, Int32 > (ref e) & flagsValue) == flagsValue;
#else
return ((Int32) e & (Int32) flag) == (Int32) flag;
#endif
}
/// <summary>
/// Retrieves an array of the names of the constants.
/// </summary>
/// <returns>A string array of the names of the constants.</returns>
public static string[] GetNamesFast() {
return new string[] {
"Test1",
"Test_1",
"Test2",
"Test4",
"Test5",
"Test6",
"Test7"
};
}
/// <summary>
/// Retrieves an array of the values of the constants.
/// </summary>
/// <returns>An array that contains the values of the constants.</returns>
public static TestEnum[] GetValuesFast() {
return new TestEnum[] {
TestEnum.Test1, TestEnum.Test_1, TestEnum.Test2, TestEnum.Test4, TestEnum.Test5, TestEnum.Test6, TestEnum.Test7
};
}
/// <inheritdoc cref="IsDefinedFast(TestEnum)"/>
public static bool IsDefinedFast(string value) {
_ = value ??
throw new ArgumentNullException(nameof(value));
switch (value) {
case "Test1":
return true;
case "Test2":
return true;
case "Test4":
return true;
case "Test5":
return true;
case "Test6":
return true;
case "Test7":
return true;
case "Test_1":
return true;
}
return false;
}
/// <inheritdoc cref="IsDefinedFast(TestEnum)"/>
#if NETCOREAPP3_0_OR_GREATER
#else
[MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
#endif
public static bool IsDefinedFast(Int32 value) {
return IsDefinedFast((TestEnum) value);
}
/// <summary>
/// Returns a <see cref="bool"/> telling whether its given value exists in the enumeration.
/// </summary>
/// <param name="value">The value of the enumeration constant.</param>
/// <returns><see langword="true"/> if a constant is defined with the given value from the <paramref name="value"/>.</returns>
public static bool IsDefinedFast(TestEnum value) {
switch (value) {
case TestEnum.Test1:
return true;
case TestEnum.Test2:
return true;
case TestEnum.Test4:
return true;
case TestEnum.Test5:
return true;
case TestEnum.Test6:
return true;
case TestEnum.Test7:
return true;
// Skipping duplicated constant value: TestEnum.Test_1 -> 1
}
return false;
}
/// <summary>
/// Resolves the name of the given enum value.
/// </summary>
/// <param name="e">The value of a particular enumerated constant in terms of its underlying type.</param>
/// <param name="includeFlagNames">Determines whether the value has flags, so it will return `EnumValue, EnumValue2`.</param>
/// <returns> A string containing the name of the enumerated constant or <see langword="null"/> if the enum has multiple flags set but <paramref name="includeFlagNames"/> is not enabled.</returns>
public static string ? GetNameFast(this TestEnum e, bool includeFlagNames = false) {
switch (e) {
case TestEnum.Test1:
return nameof(TestEnum.Test1);
// Skipping duplicated constant value: TestEnum.Test_1 -> 1
case TestEnum.Test2:
return nameof(TestEnum.Test2);
case TestEnum.Test4:
return nameof(TestEnum.Test4);
case TestEnum.Test5:
return nameof(TestEnum.Test5);
case TestEnum.Test6:
return nameof(TestEnum.Test6);
case TestEnum.Test7:
return nameof(TestEnum.Test7);
}
// Returning null is the default behavior.
if (!includeFlagNames)
return null;
//throw new Exception("Enum name could not be found!");
var flagBuilder = new StringBuilder();
if (e.HasFlagFast(TestEnum.Test1))
flagBuilder.Append(TestEnum.Test1.GetNameFast(false) + ", ");
// Skipping duplicated constant value: TestEnum.Test_1 -> 1
if (e.HasFlagFast(TestEnum.Test2))
flagBuilder.Append(TestEnum.Test2.GetNameFast(false) + ", ");
if (e.HasFlagFast(TestEnum.Test4))
flagBuilder.Append(TestEnum.Test4.GetNameFast(false) + ", ");
if (e.HasFlagFast(TestEnum.Test5))
flagBuilder.Append(TestEnum.Test5.GetNameFast(false) + ", ");
if (e.HasFlagFast(TestEnum.Test6))
flagBuilder.Append(TestEnum.Test6.GetNameFast(false) + ", ");
if (e.HasFlagFast(TestEnum.Test7))
flagBuilder.Append(TestEnum.Test7.GetNameFast(false) + ", ");
return flagBuilder.ToString().Trim(new char[] {
',',
' '
});
}
/// <summary>
/// Converts the value of this instance to its equivalent string representation.
/// </summary>
/// <param name="e">The value of a particular enumerated constant in terms of its underlying type.</param>
/// <returns>The string representation of the value of this instance.</returns>
#if NETCOREAPP3_0_OR_GREATER
#else
[MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
#endif
public static string ? ToStringFast(this TestEnum e) {
return GetNameFast(e, true);
}
/// <summary>
/// Converts the string representation of the name or numeric value of one or more enumerated constants to an equivalent enumerated object.
/// </summary>
/// <param name="value">A string containing the name or value to convert.</param>
/// <param name="ignoreCase"><see langword="true"/> to ignore case; false to regard case.</param>
/// <param name="result">The result of the enumeration constant.</param>
/// <returns><see langword="true"/> if the conversion succeeded; <see langword="false"/> otherwise.</returns>
public static bool TryParseFast(string value, bool ignoreCase, out TestEnum result) {
result = ParseFast(out
var successful, value: value, ignoreCase: ignoreCase, throwOnFailure: false);
return successful;
}
/// <summary>
/// Converts the string representation of the name or numeric value of one or more enumerated constants to an equivalent enumerated object.
/// </summary>
/// <param name="value">A string containing the name or value to convert.</param>
/// <param name="ignoreCase"><see langword="true"/> to ignore case; false to regard case.</param>
/// <returns>The enumeration value whose value is represented by the given value.</returns>
public static TestEnum ParseFast(string value, bool ignoreCase = false) {
return ParseFast(out _, value: value, ignoreCase: ignoreCase, throwOnFailure: true);
}
/// <summary>
/// Converts the string representation of the name or numeric value of one or more enumerated constants to an equivalent enumerated object.
/// </summary>
/// <param name="successful"><see langword="true"/> if the conversion succeeded; <see langword="false"/> otherwise.</param>
/// <param name="value">A string containing the name or value to convert.</param>
/// <param name="ignoreCase"><see langword="true"/> to ignore case; false to regard case.</param>
/// <param name="throwOnFailure">Determines whether to throw an <see cref="Exception"/> on errors or not.</param>
/// <returns>The enumeration value whose value is represented by the given value.</returns>
public static TestEnum ParseFast(out bool successful, string value, bool ignoreCase = false, bool throwOnFailure = true) {
successful = false;
if (throwOnFailure && (string.IsNullOrEmpty(value) || string.IsNullOrWhiteSpace(value))) {
throw new ArgumentException("Value can't be null or whitespace!", nameof(value));
}
Int32 localResult = 0;
bool parsed = false;
string subValue;
string originalValue = value;
char firstChar = value[0];
if (char.IsDigit(firstChar) || firstChar == '-' || firstChar == '+') {
if (Int32.TryParse(value, NumberStyles.AllowLeadingSign | NumberStyles.AllowTrailingWhite, null, out
var valueNumber))
switch (valueNumber) {
case (Int32) TestEnum.Test1:
successful = true;
return TestEnum.Test1;
case (Int32) TestEnum.Test2:
successful = true;
return TestEnum.Test2;
case (Int32) TestEnum.Test4:
successful = true;
return TestEnum.Test4;
case (Int32) TestEnum.Test5:
successful = true;
return TestEnum.Test5;
case (Int32) TestEnum.Test6:
successful = true;
return TestEnum.Test6;
case (Int32) TestEnum.Test7:
successful = true;
return TestEnum.Test7;
// Skipping duplicated constant value: TestEnum.Test_1 -> 1
}
} else
while (value != null && value.Length > 0) {
parsed = false;
int endIndex = value.IndexOf(',');
if (endIndex < 0) {
// No next separator; use the remainder as the next value.
subValue = value.Trim();
value = null!;
} else if (endIndex != value!.Length - 1) {
// Found a separator before the last char.
subValue = value.Substring(0, endIndex).Trim();
value = value.Substring(endIndex + 1);
} else {
// Last char was a separator, which is invalid.
break;
}
if (!ignoreCase) {
switch (subValue) {
case nameof(TestEnum.Test1):
parsed = true;
localResult |= (Int32) TestEnum.Test1;
break;
case nameof(TestEnum.Test2):
parsed = true;
localResult |= (Int32) TestEnum.Test2;
break;
case nameof(TestEnum.Test4):
parsed = true;
localResult |= (Int32) TestEnum.Test4;
break;
case nameof(TestEnum.Test5):
parsed = true;
localResult |= (Int32) TestEnum.Test5;
break;
case nameof(TestEnum.Test6):
parsed = true;
localResult |= (Int32) TestEnum.Test6;
break;
case nameof(TestEnum.Test7):
parsed = true;
localResult |= (Int32) TestEnum.Test7;
break;
case nameof(TestEnum.Test_1):
parsed = true;
localResult |= (Int32) TestEnum.Test_1;
break;
}
} else {
if (subValue.Equals("Test1", StringComparison.OrdinalIgnoreCase)) {
parsed = true;
localResult |= (Int32) TestEnum.Test1;
}
if (subValue.Equals("Test2", StringComparison.OrdinalIgnoreCase)) {
parsed = true;
localResult |= (Int32) TestEnum.Test2;
}
if (subValue.Equals("Test4", StringComparison.OrdinalIgnoreCase)) {
parsed = true;
localResult |= (Int32) TestEnum.Test4;
}
if (subValue.Equals("Test5", StringComparison.OrdinalIgnoreCase)) {
parsed = true;
localResult |= (Int32) TestEnum.Test5;
}
if (subValue.Equals("Test6", StringComparison.OrdinalIgnoreCase)) {
parsed = true;
localResult |= (Int32) TestEnum.Test6;
}
if (subValue.Equals("Test7", StringComparison.OrdinalIgnoreCase)) {
parsed = true;
localResult |= (Int32) TestEnum.Test7;
}
if (subValue.Equals("Test_1", StringComparison.OrdinalIgnoreCase)) {
parsed = true;
localResult |= (Int32) TestEnum.Test_1;
}
}
if (!parsed)
break;
}
successful = true;
if (!parsed) {
successful = false;
if (throwOnFailure)
throw new ArgumentException($"Could not convert the given value `{originalValue}`.", nameof(value));
}
return (TestEnum) localResult;
}
}
}
#nullable restore