From 4b94a328c884fd188f3177e34dbb50939156b518 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 20 Jul 2026 18:19:40 +0000 Subject: [PATCH 1/6] Initial plan From d610f468799c736dfecf7549ea4fd3f0518a9369 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 20 Jul 2026 18:26:09 +0000 Subject: [PATCH 2/6] Skip underscore back-compat restoration when custom code defines member Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com> --- .../src/Providers/EnumProvider.cs | 17 +++++++++-- .../src/Providers/ExtensibleEnumProvider.cs | 8 ++++- .../EnumProviders/EnumProviderTests.cs | 29 +++++++++++++++++++ .../MockInputEnum.cs | 9 ++++++ .../MockInputEnum.cs | 10 +++++++ 5 files changed, 70 insertions(+), 3 deletions(-) create mode 100644 packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/BackCompat_ExtensibleEnumCustomCodeTakesPrecedenceOverPreservedUnderscores(Custom)/MockInputEnum.cs create mode 100644 packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/BackCompat_ExtensibleEnumCustomCodeTakesPrecedenceOverPreservedUnderscores(Last)/MockInputEnum.cs diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/EnumProvider.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/EnumProvider.cs index e164342ed20..e70e27fd52d 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/EnumProvider.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/EnumProvider.cs @@ -70,7 +70,8 @@ protected override string BuildNamespace() => string.IsNullOrEmpty(_inputType?.N private protected static string GetBackCompatibleName( string generatedName, IReadOnlyList generatedNames, - IReadOnlyList lastContractNames) + IReadOnlyList lastContractNames, + IReadOnlyCollection? customMemberNames = null) { if (lastContractNames.Any(n => n.Equals(generatedName, StringComparison.OrdinalIgnoreCase))) { @@ -90,9 +91,21 @@ private protected static string GetBackCompatibleName( .Take(2) .ToArray(); - return matchingCurrentNames.Length == 1 && matchingLastContractNames.Length == 1 + var backCompatName = matchingCurrentNames.Length == 1 && matchingLastContractNames.Length == 1 ? matchingLastContractNames[0] : generatedName; + + // If restoring the underscore-preserved name would collide with a member that already + // exists in custom code, keep the generated name so the custom member is preserved + // rather than duplicated or removed. + if (customMemberNames != null + && !backCompatName.Equals(generatedName, StringComparison.Ordinal) + && customMemberNames.Contains(backCompatName)) + { + return generatedName; + } + + return backCompatName; } protected override bool GetIsEnum() => true; diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ExtensibleEnumProvider.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ExtensibleEnumProvider.cs index c849dcf2a1b..9e735a81df0 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ExtensibleEnumProvider.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ExtensibleEnumProvider.cs @@ -52,6 +52,12 @@ protected override IReadOnlyList BuildEnumValues() .Select(v => v.IsExactName ? v.Name : v.Name.ToIdentifierName()) .ToArray(); var lastContractNames = LastContractView?.Properties.Select(p => p.Name).ToArray() ?? []; + // Members already implemented in custom code must not be reintroduced by the underscore + // back-compat restoration, otherwise the generated member would collide with (and + // effectively remove) the custom implementation. + var customMemberNames = new HashSet( + CustomCodeView?.Properties.Select(p => p.Name) ?? [], + StringComparer.Ordinal); var values = new EnumTypeMember[_allowedValues.Count]; for (int i = 0; i < _allowedValues.Count; i++) @@ -60,7 +66,7 @@ protected override IReadOnlyList BuildEnumValues() // build the field var modifiers = FieldModifiers.Private | FieldModifiers.Const; // the fields for extensible enums are private and const, storing the underlying values, therefore we need to append the word `Value` to the name - var valueName = GetBackCompatibleName(generatedNames[i], generatedNames, lastContractNames); + var valueName = GetBackCompatibleName(generatedNames[i], generatedNames, lastContractNames, customMemberNames); var name = $"{valueName}Value"; // for initializationValue, if the enum is extensible, we always need it var initializationValue = Literal(inputValue.Value); diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/EnumProviderTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/EnumProviderTests.cs index 4fd64c9b4c6..d5286dfb2ff 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/EnumProviderTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/EnumProviderTests.cs @@ -843,6 +843,35 @@ await MockHelpers.LoadMockGeneratorAsync( Assert.AreEqual("Other", enumType.Properties[1].Name); } + // Verifies that when custom code already implements the underscore-named member (e.g. the + // user manually re-added 'Existing_Value' pointing at the generated 'ExistingValue'), the + // underscore back-compat restoration does NOT rename the generated member. Otherwise the + // generated member would collide with (and effectively remove) the custom implementation. + [Test] + public async Task BackCompat_ExtensibleEnumCustomCodeTakesPrecedenceOverPreservedUnderscores() + { + await MockHelpers.LoadMockGeneratorAsync( + createCSharpTypeCore: (inputType) => typeof(string), + compilation: async () => await Helpers.GetCompilationFromDirectoryAsync(parameters: "Custom"), + lastContractCompilation: async () => await Helpers.GetCompilationFromDirectoryAsync(parameters: "Last")); + + var input = InputFactory.StringEnum("mockInputEnum", [ + ("ExistingValue", "existing"), + ("Other", "other"), + ], isExtensible: true); + + var enumType = EnumProvider.Create(input); + Assert.IsNotNull(enumType.CustomCodeView); + Assert.IsTrue(enumType.CustomCodeView!.Properties.Any(p => p.Name == "Existing_Value")); + + // The generated member keeps its non-underscore name so the custom 'Existing_Value' + // member (which references the generated 'ExistingValue') is preserved. + Assert.AreEqual("ExistingValueValue", enumType.Fields[1].Name); + Assert.AreEqual("OtherValue", enumType.Fields[2].Name); + Assert.AreEqual("ExistingValue", enumType.Properties[0].Name); + Assert.AreEqual("Other", enumType.Properties[1].Name); + } + // Verifies that back-compat does NOT re-introduce enum values that have been suppressed // via [CodeGenSuppress] or that already exist in user-provided custom code. Without // filtering in ProcessTypeForBackCompatibility, the back-compat code would rebuild the diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/BackCompat_ExtensibleEnumCustomCodeTakesPrecedenceOverPreservedUnderscores(Custom)/MockInputEnum.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/BackCompat_ExtensibleEnumCustomCodeTakesPrecedenceOverPreservedUnderscores(Custom)/MockInputEnum.cs new file mode 100644 index 00000000000..7c7184bd735 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/BackCompat_ExtensibleEnumCustomCodeTakesPrecedenceOverPreservedUnderscores(Custom)/MockInputEnum.cs @@ -0,0 +1,9 @@ +#nullable disable + +namespace Sample.Models +{ + public readonly partial struct MockInputEnum + { + public static MockInputEnum Existing_Value { get; } = ExistingValue; + } +} diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/BackCompat_ExtensibleEnumCustomCodeTakesPrecedenceOverPreservedUnderscores(Last)/MockInputEnum.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/BackCompat_ExtensibleEnumCustomCodeTakesPrecedenceOverPreservedUnderscores(Last)/MockInputEnum.cs new file mode 100644 index 00000000000..5df3fb5b33a --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/BackCompat_ExtensibleEnumCustomCodeTakesPrecedenceOverPreservedUnderscores(Last)/MockInputEnum.cs @@ -0,0 +1,10 @@ +#nullable disable + +namespace Sample.Models +{ + public readonly partial struct MockInputEnum + { + public static MockInputEnum Existing_Value { get; } + public static MockInputEnum Other { get; } + } +} From 2f24c611d33db1dfe2d623ca3241a4b98f0b1606 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 20 Jul 2026 18:28:41 +0000 Subject: [PATCH 3/6] Use case-insensitive custom member match; verify Cop and tests pass Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com> --- .../src/Providers/ExtensibleEnumProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ExtensibleEnumProvider.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ExtensibleEnumProvider.cs index 9e735a81df0..daa872ef5a8 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ExtensibleEnumProvider.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ExtensibleEnumProvider.cs @@ -57,7 +57,7 @@ protected override IReadOnlyList BuildEnumValues() // effectively remove) the custom implementation. var customMemberNames = new HashSet( CustomCodeView?.Properties.Select(p => p.Name) ?? [], - StringComparer.Ordinal); + StringComparer.OrdinalIgnoreCase); var values = new EnumTypeMember[_allowedValues.Count]; for (int i = 0; i < _allowedValues.Count; i++) From 091092aea09522ef36ed09f17d3151571a18bace Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 20 Jul 2026 19:26:36 +0000 Subject: [PATCH 4/6] Compute custom member names inside GetBackCompatibleName; validate via testdata Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com> --- .../src/Providers/EnumProvider.cs | 18 +++++--- .../src/Providers/ExtensibleEnumProvider.cs | 8 +--- .../EnumProviders/EnumProviderTests.cs | 6 +-- ...TakesPrecedenceOverPreservedUnderscores.cs | 46 +++++++++++++++++++ 4 files changed, 61 insertions(+), 17 deletions(-) create mode 100644 packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/BackCompat_ExtensibleEnumCustomCodeTakesPrecedenceOverPreservedUnderscores.cs diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/EnumProvider.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/EnumProvider.cs index e70e27fd52d..d3ff4285fd7 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/EnumProvider.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/EnumProvider.cs @@ -67,11 +67,18 @@ protected override string BuildNamespace() => string.IsNullOrEmpty(_inputType?.N protected static string RemoveUnderscores(string name) => name.Replace("_", string.Empty); - private protected static string GetBackCompatibleName( + private HashSet? _customMemberNames; + // Names of members already implemented in custom code. Combines custom properties (used by + // extensible enums) and custom fields (used by fixed enums) so the same guard works for both. + private HashSet CustomMemberNames => _customMemberNames ??= new HashSet( + (CustomCodeView?.Properties.Select(p => p.Name) ?? []) + .Concat(CustomCodeView?.Fields.Select(f => f.Name) ?? []), + StringComparer.OrdinalIgnoreCase); + + private protected string GetBackCompatibleName( string generatedName, IReadOnlyList generatedNames, - IReadOnlyList lastContractNames, - IReadOnlyCollection? customMemberNames = null) + IReadOnlyList lastContractNames) { if (lastContractNames.Any(n => n.Equals(generatedName, StringComparison.OrdinalIgnoreCase))) { @@ -98,9 +105,8 @@ private protected static string GetBackCompatibleName( // If restoring the underscore-preserved name would collide with a member that already // exists in custom code, keep the generated name so the custom member is preserved // rather than duplicated or removed. - if (customMemberNames != null - && !backCompatName.Equals(generatedName, StringComparison.Ordinal) - && customMemberNames.Contains(backCompatName)) + if (!backCompatName.Equals(generatedName, StringComparison.Ordinal) + && CustomMemberNames.Contains(backCompatName)) { return generatedName; } diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ExtensibleEnumProvider.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ExtensibleEnumProvider.cs index daa872ef5a8..c849dcf2a1b 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ExtensibleEnumProvider.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/ExtensibleEnumProvider.cs @@ -52,12 +52,6 @@ protected override IReadOnlyList BuildEnumValues() .Select(v => v.IsExactName ? v.Name : v.Name.ToIdentifierName()) .ToArray(); var lastContractNames = LastContractView?.Properties.Select(p => p.Name).ToArray() ?? []; - // Members already implemented in custom code must not be reintroduced by the underscore - // back-compat restoration, otherwise the generated member would collide with (and - // effectively remove) the custom implementation. - var customMemberNames = new HashSet( - CustomCodeView?.Properties.Select(p => p.Name) ?? [], - StringComparer.OrdinalIgnoreCase); var values = new EnumTypeMember[_allowedValues.Count]; for (int i = 0; i < _allowedValues.Count; i++) @@ -66,7 +60,7 @@ protected override IReadOnlyList BuildEnumValues() // build the field var modifiers = FieldModifiers.Private | FieldModifiers.Const; // the fields for extensible enums are private and const, storing the underlying values, therefore we need to append the word `Value` to the name - var valueName = GetBackCompatibleName(generatedNames[i], generatedNames, lastContractNames, customMemberNames); + var valueName = GetBackCompatibleName(generatedNames[i], generatedNames, lastContractNames); var name = $"{valueName}Value"; // for initializationValue, if the enum is extensible, we always need it var initializationValue = Literal(inputValue.Value); diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/EnumProviderTests.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/EnumProviderTests.cs index d5286dfb2ff..6aa43dc6ac8 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/EnumProviderTests.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/EnumProviderTests.cs @@ -866,10 +866,8 @@ await MockHelpers.LoadMockGeneratorAsync( // The generated member keeps its non-underscore name so the custom 'Existing_Value' // member (which references the generated 'ExistingValue') is preserved. - Assert.AreEqual("ExistingValueValue", enumType.Fields[1].Name); - Assert.AreEqual("OtherValue", enumType.Fields[2].Name); - Assert.AreEqual("ExistingValue", enumType.Properties[0].Name); - Assert.AreEqual("Other", enumType.Properties[1].Name); + var content = new TypeProviderWriter(enumType).Write().Content; + Assert.AreEqual(Helpers.GetExpectedFromFile(), content); } // Verifies that back-compat does NOT re-introduce enum values that have been suppressed diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/BackCompat_ExtensibleEnumCustomCodeTakesPrecedenceOverPreservedUnderscores.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/BackCompat_ExtensibleEnumCustomCodeTakesPrecedenceOverPreservedUnderscores.cs new file mode 100644 index 00000000000..1a44d0e63a9 --- /dev/null +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/test/Providers/EnumProviders/TestData/EnumProviderTests/BackCompat_ExtensibleEnumCustomCodeTakesPrecedenceOverPreservedUnderscores.cs @@ -0,0 +1,46 @@ +// + +#nullable disable + +using System; +using System.ComponentModel; +using Sample; + +namespace Sample.Models +{ + public readonly partial struct MockInputEnum : global::System.IEquatable + { + private readonly string _value; + private const string ExistingValueValue = "existing"; + private const string OtherValue = "other"; + + public MockInputEnum(string value) + { + global::Sample.Argument.AssertNotNull(value, nameof(value)); + + _value = value; + } + + public static global::Sample.Models.MockInputEnum ExistingValue { get; } = new global::Sample.Models.MockInputEnum(ExistingValueValue); + + public static global::Sample.Models.MockInputEnum Other { get; } = new global::Sample.Models.MockInputEnum(OtherValue); + + public static bool operator ==(global::Sample.Models.MockInputEnum left, global::Sample.Models.MockInputEnum right) => left.Equals(right); + + public static bool operator !=(global::Sample.Models.MockInputEnum left, global::Sample.Models.MockInputEnum right) => !left.Equals(right); + + public static implicit operator global::Sample.Models.MockInputEnum(string value) => new global::Sample.Models.MockInputEnum(value); + + public static implicit operator global::Sample.Models.MockInputEnum?(string value) => (value == null) ? null : new global::Sample.Models.MockInputEnum(value); + + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] + public override bool Equals(object obj) => ((obj is global::Sample.Models.MockInputEnum other) && this.Equals(other)); + + public bool Equals(global::Sample.Models.MockInputEnum other) => string.Equals(_value, other._value, global::System.StringComparison.InvariantCultureIgnoreCase); + + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Never)] + public override int GetHashCode() => (_value != null) ? global::System.StringComparer.InvariantCultureIgnoreCase.GetHashCode(_value) : 0; + + public override string ToString() => _value; + } +} From 5de89324d1e43ac9a0c808f0a1c0e74c77fe1329 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 20 Jul 2026 19:42:24 +0000 Subject: [PATCH 5/6] Select custom members by provider type; early-return when no last contract Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com> --- .../src/Providers/EnumProvider.cs | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/EnumProvider.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/EnumProvider.cs index d3ff4285fd7..951ee136777 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/EnumProvider.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/EnumProvider.cs @@ -68,18 +68,35 @@ protected override string BuildNamespace() => string.IsNullOrEmpty(_inputType?.N protected static string RemoveUnderscores(string name) => name.Replace("_", string.Empty); private HashSet? _customMemberNames; - // Names of members already implemented in custom code. Combines custom properties (used by - // extensible enums) and custom fields (used by fixed enums) so the same guard works for both. + // Names of members already implemented in custom code. Extensible enums expose their members + // as properties while fixed enums expose them as fields, so we inspect the appropriate set + // based on the concrete provider type. private HashSet CustomMemberNames => _customMemberNames ??= new HashSet( - (CustomCodeView?.Properties.Select(p => p.Name) ?? []) - .Concat(CustomCodeView?.Fields.Select(f => f.Name) ?? []), + GetCustomMemberNames(), StringComparer.OrdinalIgnoreCase); + private IEnumerable GetCustomMemberNames() + { + if (CustomCodeView is null) + { + return []; + } + + return IsExtensible + ? CustomCodeView.Properties.Select(p => p.Name) + : CustomCodeView.Fields.Select(f => f.Name); + } + private protected string GetBackCompatibleName( string generatedName, IReadOnlyList generatedNames, IReadOnlyList lastContractNames) { + if (lastContractNames.Count == 0) + { + return generatedName; + } + if (lastContractNames.Any(n => n.Equals(generatedName, StringComparison.OrdinalIgnoreCase))) { return generatedName; From 0f7dcfda265a56ee10ebc5a506a0cb28d898462f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 20 Jul 2026 19:55:21 +0000 Subject: [PATCH 6/6] Remove explanatory comment on CustomMemberNames helper Co-authored-by: jorgerangel-msft <102122018+jorgerangel-msft@users.noreply.github.com> --- .../Microsoft.TypeSpec.Generator/src/Providers/EnumProvider.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/EnumProvider.cs b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/EnumProvider.cs index 951ee136777..e8496d8987e 100644 --- a/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/EnumProvider.cs +++ b/packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator/src/Providers/EnumProvider.cs @@ -68,9 +68,6 @@ protected override string BuildNamespace() => string.IsNullOrEmpty(_inputType?.N protected static string RemoveUnderscores(string name) => name.Replace("_", string.Empty); private HashSet? _customMemberNames; - // Names of members already implemented in custom code. Extensible enums expose their members - // as properties while fixed enums expose them as fields, so we inspect the appropriate set - // based on the concrete provider type. private HashSet CustomMemberNames => _customMemberNames ??= new HashSet( GetCustomMemberNames(), StringComparer.OrdinalIgnoreCase);