强制枚举具有明确的整数值

huangapple go评论73阅读模式
英文:

Enforce Enums to have explicit int value

问题

我想强制要求我的代码库中的所有枚举都要分配明确的值。许多枚举以它们的整数值(在数据库、文件或队列中)存储在休眠状态下,因此在中间添加新值会重新排序后续的值,导致非常不好的情况。

我不反对编写一个Roslyn分析器来完成这项工作,我考虑过编写单元测试,但我认为反射API不会告诉我枚举是否分配了隐式或显式的值。

另一种方法可能是使用类似Verify的批准/快照测试,其中枚举的值会反射并明确列在测试文件中,如果添加新的枚举或枚举值,测试将失败,直到您批准它。

快照测试似乎有点过于繁琐,所以Roslyn分析器将为当时正在开发的开发人员提供更好的反馈。

不好的示例:

public enum ImplictValuedEnum
{
    Zero,
    One,
    Two,
    Three
}

好的示例:

public enum ExplictValuedEnum
{
    Zero = 0,
    One = 1,
    Two = 2,
    Three = 3
}
英文:

I would like to enforce that all enums in my codebase to have explicit values assigned. Many Enums are stored at-rest using their int value (in a DB or file or queue) so adding a new value in the middle reorders the following values and makes for a very sad time.

I'm not opposed to writing a roslyn analyser to get this done, I considered a unit test, but I don't think the reflection APIs will tell me if there is an implicit or explicit value assigned to the enum.

Another approach could be an approval/snapshot test using something like Verify where the values are reflected and explicitly listed in a test file and if you add a new enum or enum value the test fails until you approve it.

The snapshot test seems heavy-handed, so a roslyn analyser would give better feedback to the dev working at the time.

Bad:

public enum ImplictValuedEnum
{
    Zero,
    One,
    Two,
    Three
}

Good:

public enum ExplictValuedEnum
{
    Zero = 0,
    One = 1,
    Two = 2,
    Three = 3
}

答案1

得分: 0

微软提供了一个用于分析/修复 Roslyn 代码的基本模板/示例。链接在这里:https://github.com/dotnet/samples/tree/main/csharp/roslyn-sdk/Tutorials

这是一个简化版的 Roslyn 分析器,用于解决这个问题。

[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class ExplicitEnumValueAnalyzer : DiagnosticAnalyzer
{
    public const string DiagnosticId = "ExplicitEnumValue";

    public const string Title = "Enum Members must have an explicit value set";
    public const string MessageFormat = "{0}.{1} must have an explicit value";

    public const string Description =
        "Enum values can be serialized as integer values and stored at rest. If members are added or removed from the enum existing implicit values may change, corrupting the at-rest data.";

    public const string Category = "Usage";

    private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(
        DiagnosticId,
        Title,
        MessageFormat,
        Category,
        DiagnosticSeverity.Error,
        isEnabledByDefault: true,
        description: Description);

    public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Rule);

    public override void Initialize(AnalysisContext context)
    {
        // See https://github.com/dotnet/roslyn/blob/main/docs/analyzers/Analyzer%20Actions%20Semantics.md for more information
        context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
        context.EnableConcurrentExecution();
        context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.EnumMemberDeclaration);
    }

    private void AnalyzeNode(SyntaxNodeAnalysisContext context)
    {
        if (!(context.Node is EnumMemberDeclarationSyntax enumNode))
            return;

        //has equals sign, so has an explicit value
        if (!(enumNode.EqualsValue is null))
            return;

        var enumDeclaration = enumNode.Parent as EnumDeclarationSyntax;
        var enumName = enumDeclaration?.Identifier.Text ?? "UnknownEnum";
        var enumMember = enumNode.Identifier.Text;

        context.ReportDiagnostic(
            Diagnostic.Create(
                Rule,
                context.Node.GetLocation(),
                //the following arguments are used in the MessageFormat string above
                enumName,
                enumMember));
    }
}
英文:

Microsoft do provide a basic template/sample for roylsn code analysers/fixers. https://github.com/dotnet/samples/tree/main/csharp/roslyn-sdk/Tutorials

Here's a simplified roslyn analyser of what I came up with to address this.

[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class ExplicitEnumValueAnalyzer : DiagnosticAnalyzer
{
    public const string DiagnosticId = &quot;ExplicitEnumValue&quot;;

    public const string Title = &quot;Enum Members must have an explicit value set&quot;;
    public const string MessageFormat = &quot;{0}.{1} must have an explicit value&quot;;

    public const string Description =
        &quot;Enum values can be serialized as integer values and stored at rest. If members are added or removed from the enum existing implicit values may change, corrupting the at-rest data.&quot;;

    public const string Category = &quot;Usage&quot;;

    private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(
        DiagnosticId,
        Title,
        MessageFormat,
        Category,
        DiagnosticSeverity.Error,
        isEnabledByDefault: true,
        description: Description);

    public override ImmutableArray&lt;DiagnosticDescriptor&gt; SupportedDiagnostics =&gt; ImmutableArray.Create(Rule);

    public override void Initialize(AnalysisContext context)
    {
        // See https://github.com/dotnet/roslyn/blob/main/docs/analyzers/Analyzer%20Actions%20Semantics.md for more information
        context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
        context.EnableConcurrentExecution();
        context.RegisterSyntaxNodeAction(AnalyzeNode, SyntaxKind.EnumMemberDeclaration);
    }

    private void AnalyzeNode(SyntaxNodeAnalysisContext context)
    {
        if (!(context.Node is EnumMemberDeclarationSyntax enumNode))
            return;

        //has equals sign, so has an explicit value
        if (!(enumNode.EqualsValue is null))
            return;

        var enumDeclaration = enumNode.Parent as EnumDeclarationSyntax;
        var enumName = enumDeclaration?.Identifier.Text ?? &quot;UnknownEnum&quot;;
        var enumMember = enumNode.Identifier.Text;

        context.ReportDiagnostic(
            Diagnostic.Create(
                Rule,
                context.Node.GetLocation(),
                //the following arguments are used in the MessageFormat string above
                enumName,
                enumMember));
    }
}

huangapple
  • 本文由 发表于 2023年6月16日 09:20:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/76486414.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定