From a292346f808104b30b8378d4e895b766e0f452ef Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 5 Aug 2025 17:37:03 +0000 Subject: [PATCH 1/3] Initial plan From a822915702e5f7df75a3f90b0ce3f06644faf7ff Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 5 Aug 2025 17:44:04 +0000 Subject: [PATCH 2/3] Add documentation and examples for enum zero conversion dangers Co-authored-by: BillWagner <493969+BillWagner@users.noreply.github.com> --- .../language-reference/builtin-types/enum.md | 14 ++++++ .../builtin-types/snippets/shared/EnumType.cs | 47 +++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/docs/csharp/language-reference/builtin-types/enum.md b/docs/csharp/language-reference/builtin-types/enum.md index 2fb7ebfa3eea2..0dd3b0bd4e2d9 100644 --- a/docs/csharp/language-reference/builtin-types/enum.md +++ b/docs/csharp/language-reference/builtin-types/enum.md @@ -42,6 +42,20 @@ You cannot define a method inside the definition of an enumeration type. To add The default value of an enumeration type `E` is the value produced by expression `(E)0`, even if zero doesn't have the corresponding enum member. +## Implicit conversions from zero + +C# allows implicit conversions from the literal value `0` to any enum type, and from `const` values equal to zero. This behavior can lead to unexpected results when an enum doesn't include a member with the value zero: + +[!code-csharp[zero conversions](snippets/shared/EnumType.cs#ZeroConversions)] + +In the preceding example, both `port1` and `port2` are assigned the value `0`, but `GpioPort` has no member with that value. The method confirms these are invalid enum values. + +This implicit conversion exists for backward compatibility, but it can introduce bugs in your code. To avoid these issues: + +- Consider defining a member with value `0` in your enums when appropriate. +- Use to validate enum values when converting from numeric types. +- Be cautious when using numeric parameters that might be implicitly converted to enum types. + You use an enumeration type to represent a choice from a set of mutually exclusive values or a combination of choices. To represent a combination of choices, define an enumeration type as bit flags. ## Enumeration types as bit flags diff --git a/docs/csharp/language-reference/builtin-types/snippets/shared/EnumType.cs b/docs/csharp/language-reference/builtin-types/snippets/shared/EnumType.cs index 4016a3a6f3d4d..0b638c3cbc8cf 100644 --- a/docs/csharp/language-reference/builtin-types/snippets/shared/EnumType.cs +++ b/docs/csharp/language-reference/builtin-types/snippets/shared/EnumType.cs @@ -8,6 +8,7 @@ public static void Examples() { FlagsEnumExample.Main(); EnumConversionExample.Main(); + ZeroConversionExample.Main(); } // @@ -76,4 +77,50 @@ public static void Main() } } // + + // + public enum GpioPort + { + GpioA = 1, + GpioB, + GpioC, + GpioD + } + + public class ZeroConversionExample + { + public static void Main() + { + // This compiles without warning but creates an invalid enum value + GpioPort port1 = (GpioPort)0; + Console.WriteLine($"port1: {port1}"); // Output: port1: 0 + + // This also compiles due to implicit conversion from zero + GpioPort port2 = GetPort(0); + Console.WriteLine($"port2: {port2}"); // Output: port2: 0 + + // Check if the enum value is valid + bool isValid1 = Enum.IsDefined(typeof(GpioPort), port1); + bool isValid2 = Enum.IsDefined(typeof(GpioPort), port2); + Console.WriteLine($"port1 is valid: {isValid1}"); // Output: port1 is valid: False + Console.WriteLine($"port2 is valid: {isValid2}"); // Output: port2 is valid: False + + // Safer approach - validate enum values + if (Enum.IsDefined(typeof(GpioPort), 0)) + { + GpioPort safePort = (GpioPort)0; + } + else + { + Console.WriteLine("Value 0 is not a valid GpioPort"); + // Handle the invalid case appropriately + } + } + + public static GpioPort GetPort(GpioPort port) + { + return port; + } + } + // } From 0a6a35963ded02d73b92f6f41c3c8d5d68f46f63 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 6 Aug 2025 18:17:22 +0000 Subject: [PATCH 3/3] Address review feedback: use ::: syntax, fix explanation, strengthen recommendation Co-authored-by: BillWagner <493969+BillWagner@users.noreply.github.com> --- docs/csharp/language-reference/builtin-types/enum.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/csharp/language-reference/builtin-types/enum.md b/docs/csharp/language-reference/builtin-types/enum.md index 0dd3b0bd4e2d9..236c8958cc45a 100644 --- a/docs/csharp/language-reference/builtin-types/enum.md +++ b/docs/csharp/language-reference/builtin-types/enum.md @@ -46,13 +46,13 @@ The default value of an enumeration type `E` is the value produced by expression C# allows implicit conversions from the literal value `0` to any enum type, and from `const` values equal to zero. This behavior can lead to unexpected results when an enum doesn't include a member with the value zero: -[!code-csharp[zero conversions](snippets/shared/EnumType.cs#ZeroConversions)] +:::code language="csharp" source="snippets/shared/EnumType.cs" id="SnippetZeroConversions"::: In the preceding example, both `port1` and `port2` are assigned the value `0`, but `GpioPort` has no member with that value. The method confirms these are invalid enum values. -This implicit conversion exists for backward compatibility, but it can introduce bugs in your code. To avoid these issues: +This implicit conversion exists because the 0 bit pattern is the default for all struct types, including all enum types. However, it can introduce bugs in your code. To avoid these issues: -- Consider defining a member with value `0` in your enums when appropriate. +- You should almost always define a member with value `0` in your enums. - Use to validate enum values when converting from numeric types. - Be cautious when using numeric parameters that might be implicitly converted to enum types.