You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C#中实现检查集合唯一性的自定义特性?

在C#中实现集合唯一性检查的自定义特性

要实现你想要的效果,C#原生自定义特性仅作为元数据标记,无法直接触发编译时检查——你需要借助Roslyn静态代码分析器来扫描带特性的属性,验证初始化集合的唯一性。下面是具体实现步骤:

1. 定义标记用的自定义特性

先创建一个特性类,指定它可应用在属性上:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class HasUniqueItemsAttribute : Attribute
{
}

2. 编写Roslyn编译时分析器

Roslyn分析器能在编译阶段扫描代码,识别带HasUniqueItemsAttribute的属性,并检查其初始化集合的元素是否重复。核心逻辑如下:

  • 筛选出所有标记了目标特性的属性声明
  • 解析属性的初始化表达式,判断是否为集合初始化器
  • 遍历初始化元素,检查是否存在重复值
  • 发现重复时抛出编译错误

以下是简化版的分析器代码:

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

[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class HasUniqueItemsAnalyzer : DiagnosticAnalyzer
{
    public const string DiagnosticId = "HasUniqueItems001";
    private static readonly LocalizableString Title = "集合包含重复元素";
    private static readonly LocalizableString MessageFormat = "属性 '{0}' 的初始化集合存在重复元素: {1}";
    private static readonly LocalizableString Description = "标记了HasUniqueItemsAttribute的集合必须包含唯一元素";
    private 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)
    {
        context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
        context.EnableConcurrentExecution();
        context.RegisterSymbolAction(AnalyzeProperty, SymbolKind.Property);
    }

    private void AnalyzeProperty(SymbolAnalysisContext context)
    {
        var propertySymbol = (IPropertySymbol)context.Symbol;
        // 检查属性是否标记了目标特性
        if (!propertySymbol.GetAttributes().Any(attr => attr.AttributeClass.Name == nameof(HasUniqueItemsAttribute)))
            return;

        // 获取属性声明的语法节点
        var propertyDeclaration = propertySymbol.DeclaringSyntaxReferences.First().GetSyntax() as PropertyDeclarationSyntax;
        var initializer = propertyDeclaration?.Initializer;
        if (initializer == null)
            return;

        // 判断是否为集合初始化器
        var collectionInitializer = initializer.Value as CollectionInitializerExpressionSyntax;
        if (collectionInitializer == null)
            return;

        // 提取初始化元素的文本值(示例针对字符串,可扩展至其他类型)
        var elements = collectionInitializer.Initializers
            .Select(expr => expr.Expression as LiteralExpressionSyntax)
            .Where(lit => lit != null)
            .Select(lit => lit.Token.ValueText)
            .ToList();

        // 检查重复元素
        var duplicates = elements.GroupBy(e => e)
            .Where(g => g.Count() > 1)
            .Select(g => g.Key)
            .ToList();

        foreach (var duplicate in duplicates)
        {
            var diagnostic = Diagnostic.Create(Rule, initializer.GetLocation(), propertySymbol.Name, duplicate);
            context.ReportDiagnostic(diagnostic);
        }
    }
}

3. 应用分析器

  • 将分析器代码打包为NuGet包,或直接添加到项目中
  • 当你编写带HasUniqueItemsAttribute的属性并初始化重复元素时,编译器会直接抛出错误,实现"运行前提示"的效果

备选:运行时检查(非编译时)

如果暂时不想编写Roslyn分析器,可在属性setter中加入运行时检查,但仅能在程序运行时发现问题:

[HasUniqueItemsAttribute]
private IList<string> _myCollection;
public IList<string> MyCollection
{
    get => _myCollection;
    set
    {
        if (value.Distinct().Count() != value.Count)
            throw new ArgumentException("集合包含重复元素");
        _myCollection = value;
    }
}

内容的提问来源于stack exchange,提问作者user20291437

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 14:45:35