CA1008: Enums should have zero value

Property Value
Rule ID CA1008
Title Enums should have zero value
Category Design
Fix is breaking or non-breaking Non-breaking - When you're prompted to add a None value to a non-flag enumeration. Breaking - When you're prompted to rename or remove any enumeration values.
Enabled by default in .NET 8 No

Cause

An enumeration without an applied System.FlagsAttribute does not define a member that has a value of zero. Or, an enumeration that has an applied FlagsAttribute defines a member that has a value of zero but its name is not 'None'. Or, the enumeration defines multiple, zero-valued members.

By default, this rule only looks at externally visible enumerations, but this is configurable.

Rule description

The default value of an uninitialized enumeration, just like other value types, is zero. A non-flags-attributed enumeration should define a member that has the value of zero so that the default value is a valid value of the enumeration. If appropriate, name the member 'None' (or one of the additional permitted names). Otherwise, assign zero to the most frequently used member. By default, if the value of the first enumeration member is not set in the declaration, its value is zero.

If an enumeration that has the FlagsAttribute applied defines a zero-valued member, its name should be 'None' (or one of the additional permitted names) to indicate that no values have been set in the enumeration. Using a zero-valued member for any other purpose is contrary to the use of the FlagsAttribute in that the AND and OR bitwise operators are useless with the member. This implies that only one member should be assigned the value zero. If multiple members that have the value zero occur in a flags-attributed enumeration, Enum.ToString() returns incorrect results for members that are not zero.

How to fix violations

To fix a violation of this rule for non-flags-attributed enumerations, define a member that has the value of zero; this is a non-breaking change. For flags-attributed enumerations that define a zero-valued member, name this member 'None' and delete any other members that have a value of zero; this is a breaking change.

When to suppress warnings

Do not suppress a warning from this rule except for flags-attributed enumerations that have previously shipped.

Suppress a warning

If you just want to suppress a single violation, add preprocessor directives to your source file to disable and then re-enable the rule.

#pragma warning disable CA1008
// The code that's violating the rule is on this line.
#pragma warning restore CA1008

To disable the rule for a file, folder, or project, set its severity to none in the configuration file.

[*.{cs,vb}]
dotnet_diagnostic.CA1008.severity = none

For more information, see How to suppress code analysis warnings.

Configure code to analyze

Use the following option to configure which parts of your codebase to run this rule on.

You can configure this option for just this rule, for all rules it applies to, or for all rules in this category (Design) that it applies to. For more information, see Code quality rule configuration options.

Include specific API surfaces

You can configure which parts of your codebase to run this rule on, based on their accessibility. For example, to specify that the rule should run only against the non-public API surface, add the following key-value pair to an .editorconfig file in your project:

dotnet_code_quality.CAXXXX.api_surface = private, internal

Additional zero-value field names

In .NET 7 and later versions, you can configure other allowable names for a zero-value enumeration field, besides None. Separate multiple names by the | character. The following table shows some examples.

Option value Summary
dotnet_code_quality.CA1008.additional_enum_none_names = Never Allows both None and Never
dotnet_code_quality.CA1008.additional_enum_none_names = Never|Nothing Allows None, Never, and Nothing

Example

The following example shows two enumerations that satisfy the rule and an enumeration, BadTraceOptions, that violates the rule.

using System;

namespace ca1008
{
    public enum TraceLevel
    {
        Off = 0,
        Error = 1,
        Warning = 2,
        Info = 3,
        Verbose = 4
    }

    [Flags]
    public enum TraceOptions
    {
        None = 0,
        CallStack = 0x01,
        LogicalStack = 0x02,
        DateTime = 0x04,
        Timestamp = 0x08,
    }

    [Flags]
    public enum BadTraceOptions
    {
        CallStack = 0,
        LogicalStack = 0x01,
        DateTime = 0x02,
        Timestamp = 0x04,
    }

    class UseBadTraceOptions
    {
        static void MainTrace()
        {
            // Set the flags.
            BadTraceOptions badOptions =
               BadTraceOptions.LogicalStack | BadTraceOptions.Timestamp;

            // Check whether CallStack is set.
            if ((badOptions & BadTraceOptions.CallStack) ==
                BadTraceOptions.CallStack)
            {
                // This 'if' statement is always true.
            }
        }
    }
}
Imports System

Namespace ca1008

    Public Enum TraceLevel
        Off = 0
        AnError = 1
        Warning = 2
        Info = 3
        Verbose = 4
    End Enum

    <Flags>
    Public Enum TraceOptions
        None = 0
        CallStack = &H1
        LogicalStack = &H2
        DateTime = &H4
        Timestamp = &H8
    End Enum

    <Flags>
    Public Enum BadTraceOptions
        CallStack = 0
        LogicalStack = &H1
        DateTime = &H2
        Timestamp = &H4
    End Enum

    Class UseBadTraceOptions

        Shared Sub Main1008()

            ' Set the flags.
            Dim badOptions As BadTraceOptions =
            BadTraceOptions.LogicalStack Or BadTraceOptions.Timestamp

            ' Check whether CallStack is set.
            If ((badOptions And BadTraceOptions.CallStack) =
             BadTraceOptions.CallStack) Then
                ' This 'If' statement is always true.
            End If

        End Sub

    End Class

End Namespace

See also