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

在可空引用类型开启语境下如何编写无需空校验的IEqualityComparer

问题根源

.NET 内置的 IEqualityComparer<T> 接口的官方签名已经适配了可空引用类型规则:

  • Equals 方法的签名为 bool Equals(T? x, T? y),要求参数允许为空,你自行改为非空会违反接口契约,因此触发编译警告
  • GetHashCode 方法的签名为 int GetHashCode(T obj),默认要求参数非空

你之前尝试的fi1?.Name == fi2?.Name写法只处理了Name访问的空警告,但后续访问fi1.LastWriteTime时编译器依然认为fi1可能为空,所以警告会转移到后续代码位置,本质上没有解决你确定参数不会为空的标注需求。

以下是三种适配你内部使用场景的解决方案,均不需要额外的运行时空校验逻辑:


解法1:使用空包容运算符(!),修改成本最低

直接在你确定不可能为空的变量后加 ! 即可,告诉编译器你手动确认该值不会为空,不需要做校验。这个方案不会产生任何运行时代码,完全不影响性能,最适合内部使用的场景:

public bool Equals(FileInfo? fi1, FileInfo? fi2)
{
    return fi1!.Name == fi2!.Name &&
        RoundUp(fi1.LastWriteTime) == RoundUp(fi2.LastWriteTime) && 
        fi1.Length == fi2.Length;
}

解法2:添加调试断言,保留调试期空检查能力

如果担心调试阶段真的出现空传入的情况,可以在方法开头加 Debug.Assert,编译器会识别断言的判断逻辑,自动消除后续代码的空警告,且Release编译时断言会被完全移除,不会有额外开销:

using System.Diagnostics;

public bool Equals(FileInfo? fi1, FileInfo? fi2)
{
    Debug.Assert(fi1 != null && fi2 != null, "内部调用不允许传入空的FileInfo对象");
    return fi1.Name == fi2.Name &&
        RoundUp(fi1.LastWriteTime) == RoundUp(fi2.LastWriteTime) && 
        fi1.Length == fi2.Length;
}

解法3:给参数标注 [NotNull] 特性

如果想从方法签名层面明确参数不允许为空,可以引用 System.Diagnostics.CodeAnalysis 命名空间,给参数加 [NotNull] 特性,告知编译器该参数的调用方一定会传入非空值:

using System.Diagnostics.CodeAnalysis;

public bool Equals([NotNull] FileInfo? fi1, [NotNull] FileInfo? fi2)
{
    return fi1.Name == fi2.Name &&
        RoundUp(fi1.LastWriteTime) == RoundUp(fi2.LastWriteTime) && 
        fi1.Length == fi2.Length;
}

标注后编译器会做两项检查:

  1. 方法内部访问fi1、fi2时不再触发空警告
  2. 如果你自己的代码里有给这个Equals方法传空的调用,会触发新的警告,刚好符合你内部使用时确保传非空的要求

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 10:06:04