.net 将属性限制为每个类仅一个属性

ztmd8pv5  于 2023-06-25  发布在  .NET
关注(0)|答案(1)|浏览(112)

是否有一种方法可以确保标记为[AttributeUsage(AttributeTargets.Property)]的属性在每个类中最多使用一次?
换句话说,0或1属性都应该用特定的属性修饰,不能超过1。这能做到吗?

13z8s7eq

13z8s7eq1#

您可以创建自己的分析器,将其编译成dll并包含在您的项目中。
让我们从“测试”开始,以确保一切都如您所期望的那样工作:
单个属性类编译x1c 0d1x
第二个属性-类不编译

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

属性是独立计数的-如果其中任何一个在类中存在多次,则不会编译。


分析器错误消息x1c4d 1x
下面是分析器代码。它遍历了csharp语法,对带有标记为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);
    }
}

您必须编译Analyzer并将其dll链接到项目的csproj文件中

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

最后,我的解决方案结构

相关问题