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

优化泛型类参数声明:解决FluentValidation验证器CA1005警告

解决FluentValidation自定义验证器的CA1005警告问题

方案1:利用泛型协变简化泛型参数

IReadOnlyCollection<out T>是协变接口,可将验证器泛型参数简化为仅包含实体类型T,直接针对IReadOnlyCollection<object>验证,所有强类型只读集合都能通过协变适配:

验证器实现

using FluentValidation;
using FluentValidation.Validators;

public class CollectionSizesMatchValidator<T> : PropertyValidator<T, IReadOnlyCollection<object>>
{
    private readonly Func<T, IReadOnlyCollection<object>> _otherCollectionGetter;
    private readonly string _otherPropertyName;

    public CollectionSizesMatchValidator(Expression<Func<T, IReadOnlyCollection<object>>> otherCollectionExpr)
    {
        _otherCollectionGetter = otherCollectionExpr.Compile();
        _otherPropertyName = otherCollectionExpr.GetMemberName();
    }

    protected override ValidationResult IsValid(ValidationContext<T> context, IReadOnlyCollection<object> currentCollection)
    {
        var otherCollection = _otherCollectionGetter(context.InstanceToValidate);

        // 可根据业务需求调整null处理逻辑
        if (currentCollection == null || otherCollection == null)
        {
            return ValidationResult.Success;
        }

        if (currentCollection.Count != otherCollection.Count)
        {
            return new ValidationResult($"集合大小必须与{_otherPropertyName}一致");
        }

        return ValidationResult.Success;
    }
}

扩展方法(简化规则调用)

public static class ValidationRuleExtensions
{
    public static IRuleBuilderOptions<T, IReadOnlyCollection<TElement>> MustMatchCollectionSize<T, TElement>(
        this IRuleBuilder<T, IReadOnlyCollection<TElement>> ruleBuilder,
        Expression<Func<T, IReadOnlyCollection<object>>> otherCollectionExpr)
    {
        return ruleBuilder.SetValidator(new CollectionSizesMatchValidator<T>(otherCollectionExpr));
    }
}

使用示例

public class MyModel
{
    public IReadOnlyCollection<int> Numbers { get; set; }
    public IReadOnlyCollection<string> Names { get; set; }
}

public class MyModelValidator : AbstractValidator<MyModel>
{
    public MyModelValidator()
    {
        RuleFor(x => x.Numbers)
            .MustMatchCollectionSize(x => x.Names);
    }
}

方案2:动态适配所有只读集合类型

若需支持更多自定义只读集合类型,可通过类型检查+反射获取Count属性,进一步消除泛型参数限制:

验证器实现

using FluentValidation;
using FluentValidation.Validators;
using System.Reflection;

public class CollectionSizesMatchValidator<T> : PropertyValidator<T, object>
{
    private readonly Func<T, object> _otherCollectionGetter;
    private readonly string _otherPropertyName;

    public CollectionSizesMatchValidator(Expression<Func<T, object>> otherCollectionExpr)
    {
        _otherCollectionGetter = otherCollectionExpr.Compile();
        _otherPropertyName = otherCollectionExpr.GetMemberName();
    }

    protected override ValidationResult IsValid(ValidationContext<T> context, object currentCollection)
    {
        var otherCollection = _otherCollectionGetter(context.InstanceToValidate);

        if (!TryGetCollectionCount(currentCollection, out int currentCount) || 
            !TryGetCollectionCount(otherCollection, out int otherCount))
        {
            return new ValidationResult("待验证属性必须为只读集合类型");
        }

        if (currentCount != otherCount)
        {
            return new ValidationResult($"集合大小必须与{_otherPropertyName}一致");
        }

        return ValidationResult.Success;
    }

    private bool TryGetCollectionCount(object collection, out int count)
    {
        count = 0;
        if (collection == null) return false;

        // 优先匹配IReadOnlyCollection<T>接口
        var readOnlyCollection = collection as IReadOnlyCollection<object>;
        if (readOnlyCollection != null)
        {
            count = readOnlyCollection.Count;
            return true;
        }

        // 反射检查泛型IReadOnlyCollection<T>类型
        var type = collection.GetType();
        if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IReadOnlyCollection<>))
        {
            var countProp = type.GetProperty(nameof(IReadOnlyCollection<object>.Count));
            if (countProp != null)
            {
                count = (int)countProp.GetValue(collection);
                return true;
            }
        }

        return false;
    }
}

扩展方法

public static class ValidationRuleExtensions
{
    public static IRuleBuilderOptions<T, TProperty> MustMatchCollectionSize<T, TProperty>(
        this IRuleBuilder<T, TProperty> ruleBuilder,
        Expression<Func<T, object>> otherCollectionExpr)
        where TProperty : class
    {
        return ruleBuilder.SetValidator(new CollectionSizesMatchValidator<T>(otherCollectionExpr));
    }
}

使用示例

public class MyModelValidator : AbstractValidator<MyModel>
{
    public MyModelValidator()
    {
        RuleFor(x => x.Numbers)
            .MustMatchCollectionSize(x => x.Names);
    }
}

关键说明

  • 两种方案均将泛型参数减少至1个(仅实体类型T),彻底消除CA1005警告。
  • 方案1利用协变特性,性能更优;方案2兼容性更强,支持所有实现IReadOnlyCollection<T>的类型。
  • 无需依赖ICollection接口,完美适配IReadOnlyCollection<T>的只读特性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 17:17:54