英文:
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 = "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));
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论