You need to enable JavaScript to run this app. C#泛型类型定义中修改可空性:实现兼容可空/非空入参的NotNull扩展方法
优惠活动
大模型
产品
解决方案
定价
更多

C#泛型类型定义中修改可空性:实现兼容可空/非空入参的NotNull扩展方法

泛型可空语义适配的NotNull扩展方法实现

需求可行性结论

该需求完全可以实现,核心是利用C#可空引用类型(NRT)的编译期语义标注,在泛型签名层面拆分“入参侧的可空兼容性”和“返回侧的非空承诺”,不需要额外的变通逻辑。

原有方法的问题点

现有方法签名没有适配NRT的语义传递逻辑:当调用方传入属性类型为IEnumerable<string>?的规则构造器时,编译器会直接把TProperty推导为带可空标注的IEnumerable<string>?,导致返回的IRuleBuilder的泛型参数也保持可空标注,完全达不到“完成NotNull校验后,将属性上下文标记为非空”的效果。

具体实现方案

首先引入可空语义标注所需的命名空间:
using System.Diagnostics.CodeAnalysis;
针对本次聚焦的引用类型场景,重构后的扩展方法签名如下:

public static IRuleBuilder<TModel, TProperty> NotNull<TModel, [NotNull] TProperty>(
    this IRuleBuilderInitial<TModel, TProperty?> ruleBuilder
) where TProperty : class
{
    // 保留原有的NotNull校验注册逻辑即可
    return ruleBuilder.SetValidator(new NotNullValidator<TModel, TProperty>())!;
}

这套签名逻辑和FluentValidation 11版本之后内置的NotNull方法实现完全一致,是泛型场景下可空语义适配的标准实践,为什么能满足需求可以从两个维度拆解:

  • 入参侧兼容性:因为TProperty加了class约束(匹配引用类型场景),编译器会自动兼容两种入参情况:
    • 当传入的规则绑定的是可空引用类型IEnumerable<string>?时,TProperty会被推导为对应的非空基础类型IEnumerable<string>
    • 当传入的规则绑定的本身就是非空引用类型IEnumerable<string>时,因为非空引用类型天然兼容可空类型的参数位置,不会触发任何可空性警告
  • 返回侧非空强制:方法返回值的泛型参数直接使用推导出来的非空TProperty,因此无论入参是带不带?标注的IEnumerable<string>,最终返回的IRuleBuilder的属性泛型都会是不带可空标注的IEnumerable<string>,完全符合要求。
    其中[NotNull]标注的作用是明确告诉编译器:这个方法执行完成后,对应的属性上下文已经被保证非空,后续链式调用的所有方法都不会再触发“可能为null”的编译警告。

扩展兼容

如果需要同时兼容值类型的可空NotNull场景,只需要重载一个带值类型约束的版本即可,不会和现有引用类型的方法冲突:

// 兼容int?、Guid?等可空值类型的NotNull重载
public static IRuleBuilder<TModel, TProperty> NotNull<TModel, TProperty>(
    this IRuleBuilderInitial<TModel, TProperty?> ruleBuilder
) where TProperty : struct
{
    return ruleBuilder.SetValidator(new NotNullValidator<TModel, TProperty>())!;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.31 19:36:04