限制属性仅适用于每个类的一个属性。

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

Restrict attribute to only one property per class

问题

你是否有办法确保带有 [AttributeUsage(AttributeTargets.Property)] 标记的属性在每个类中最多只能使用一次?

换句话说,一个特定属性要么没有,要么有一个属性被装饰,绝不会超过一个。这是否可以实现?

英文:

Is there a way of ensuring that an attribute marked with [AttributeUsage(AttributeTargets.Property)] is used maximum once per class?

In other words, either 0 or 1 properties should be decorated with a particular attribute, never more than 1. Can this be done?

答案1

得分: 1

你可以创建自己的分析器,将其编译成 DLL 并包含在项目中。

让我们从“测试”开始,以确保一切都按您的预期工作:

单属性 - 类编译
限制属性仅适用于每个类的一个属性。

第二属性 - 类不编译
限制属性仅适用于每个类的一个属性。

第二个属性类型 - 不同属性一起工作并编译

限制属性仅适用于每个类的一个属性。

属性独立计数 - 如果在类中存在任何一个属性超过一次,则不编译

限制属性仅适用于每个类的一个属性。

分析器错误消息
限制属性仅适用于每个类的一个属性。

以下是分析器代码。它遍历 C# 语法,计算带有标记为 AttributeUsageAttribute 并具有标志 AttributeTargets.Property 的属性。

using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;

[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class PropertyAttributeAnalyzer : DiagnosticAnalyzer
{
    private const string Category = "Usage";

    private static DiagnosticDescriptor Rule = new DiagnosticDescriptor(
        "PA001",
        "Multiple Properties with Attribute",
        "Multiple properties with the attribute '{0}' are found in the class",
        Category,
        DiagnosticSeverity.Error,
        isEnabledByDefault: true);

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

    public override void Initialize(AnalysisContext context)
    {
        context.RegisterSyntaxNodeAction(AnalyzeClassSyntax, SyntaxKind.ClassDeclaration);
    }

    private static void AnalyzeClassSyntax(SyntaxNodeAnalysisContext context)
    {
        var classDeclaration = (ClassDeclarationSyntax)context.Node;
        var occurrences = new Dictionary<string, int>();

        foreach (var property in classDeclaration.Members.OfType<PropertyDeclarationSyntax>())
        {
            var attributes = property.AttributeLists.SelectMany(list => list.Attributes);

            foreach (var attribute in attributes)
            {
                if (!IsDesiredAttribute(context, classDeclaration, attribute))
                    continue;

                if (occurrences.ContainsKey(attribute.Name.ToString()))
                {
                    occurrences[attribute.Name.ToString()]++;
                }
                else
                {
                    occurrences[attribute.Name.ToString()] = 1;
                }
            }
        }

        foreach (var attribute in occurrences.Where(x => x.Value > 1))
        {
            var diagnostic = Diagnostic.Create(Rule, classDeclaration.Identifier.GetLocation(), attribute.Key);
            context.ReportDiagnostic(diagnostic);
        }
    }

    private static bool IsDesiredAttribute(SyntaxNodeAnalysisContext context, ClassDeclarationSyntax classDeclaration, AttributeSyntax attributeSyntax)
    {
        var semanticModel = context.SemanticModel;
        var attributeSymbol = semanticModel.GetSymbolInfo(attributeSyntax).Symbol;
        var attributeType = attributeSymbol?.ContainingType;

        if (attributeType is null)
            return false;

        var attributeUsageAttribute = semanticModel.Compilation.GetTypeByMetadataName(typeof(AttributeUsageAttribute).FullName);
        var attributeUsage = attributeType.GetAttributes().FirstOrDefault(attr => attr.AttributeClass.Equals(attributeUsageAttribute));

        if (attributeUsage is null)
            return false;

        var validOnArgument = attributeUsage.ConstructorArguments.FirstOrDefault(arg => arg.Type?.Name == "AttributeTargets");
        var validTargets = (AttributeTargets)(int)validOnArgument.Value;

        return validTargets.HasFlag(AttributeTargets.Property);
    }
}

您必须编译分析器并将其 DLL 链接到项目的 csproj 文件中。

<ItemGroup>
  <Analyzer Include="C:\Users\Asus\RiderProjects\TestAnalyzer\Analyzer\bin\Debug\net7.0\Analyzer.dll"/>
</ItemGroup>

最后,我的解决方案结构

限制属性仅适用于每个类的一个属性。

英文:

You can create your own analyzer, compile it into dll and include in your project.

Let's start with "tests" to ensure everything works as you expect:

Single property - class compiles
限制属性仅适用于每个类的一个属性。

Second property - class does not compile
限制属性仅适用于每个类的一个属性。

Second attribute type - different attributes work together and compile

限制属性仅适用于每个类的一个属性。

Attributes are counted independently - does not compile if any of those exist more than once in a class

限制属性仅适用于每个类的一个属性。
Analyzer error message
限制属性仅适用于每个类的一个属性。

Here is analyzer code. It goes over csharp syntax, counts properties with attributes that are marked with AttributeUsageAttribute and have flag AttributeTargets.Property

using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class PropertyAttributeAnalyzer : DiagnosticAnalyzer
{
private const string Category = &quot;Usage&quot;;
private static DiagnosticDescriptor Rule = new DiagnosticDescriptor(
&quot;PA001&quot;,
&quot;Multiple Properties with Attribute&quot;,
&quot;Multiple properties with the attribute &#39;{0}&#39; are found in the class&quot;,
Category,
DiagnosticSeverity.Error,
isEnabledByDefault: true);
public override ImmutableArray&lt;DiagnosticDescriptor&gt; SupportedDiagnostics { get; } = ImmutableArray.Create(Rule);
public override void Initialize(AnalysisContext context)
{
context.RegisterSyntaxNodeAction(AnalyzeClassSyntax, SyntaxKind.ClassDeclaration);
}
private static void AnalyzeClassSyntax(SyntaxNodeAnalysisContext context)
{
var classDeclaration = (ClassDeclarationSyntax)context.Node;
var occurrences = new Dictionary&lt;string, int&gt;();
foreach (var property in classDeclaration.Members.OfType&lt;PropertyDeclarationSyntax&gt;())
{
var attributes = property.AttributeLists.SelectMany(list =&gt; list.Attributes);
foreach (var attribute in attributes)
{
if (!IsDesiredAttribute(context, classDeclaration, attribute))
continue;
if (occurrences.ContainsKey(attribute.Name.ToString()))
{
occurrences[attribute.Name.ToString()]++;
}
else
{
occurrences[attribute.Name.ToString()] = 1;
}
}
}
foreach (var attribute in occurrences.Where(x =&gt; x.Value &gt; 1))
{
var diagnostic = Diagnostic.Create(Rule, classDeclaration.Identifier.GetLocation(), attribute.Key);
context.ReportDiagnostic(diagnostic);
}
}
private static bool IsDesiredAttribute(SyntaxNodeAnalysisContext context, ClassDeclarationSyntax classDeclaration, AttributeSyntax attributeSyntax)
{
var semanticModel = context.SemanticModel;
var attributeSymbol = semanticModel.GetSymbolInfo(attributeSyntax).Symbol;
var attributeType = attributeSymbol?.ContainingType;
if (attributeType is null)
return false;
var attributeUsageAttribute = semanticModel.Compilation.GetTypeByMetadataName(typeof(AttributeUsageAttribute).FullName);
var attributeUsage = attributeType.GetAttributes().FirstOrDefault(attr =&gt; attr.AttributeClass.Equals(attributeUsageAttribute));
if (attributeUsage is null)
return false;
var validOnArgument = attributeUsage.ConstructorArguments.FirstOrDefault(arg =&gt; arg.Type?.Name == &quot;AttributeTargets&quot;);
var validTargets = (AttributeTargets)(int)validOnArgument.Value;
return validTargets.HasFlag(AttributeTargets.Property);
}
}

You have to compile Analyzer and link its dll into your project's csproj file

&lt;ItemGroup&gt;
&lt;Analyzer Include=&quot;C:\Users\Asus\RiderProjects\TestAnalyzer\Analyzer\bin\Debug\net7.0\Analyzer.dll&quot;/&gt;
&lt;/ItemGroup&gt;

Finally, my solution structure

限制属性仅适用于每个类的一个属性。

huangapple
  • 本文由 发表于 2023年6月11日 22:21:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/76450918.html
匿名

发表评论

匿名网友

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

确定