为什么在Rider的 Release模式下编译时,后缀运算符 ! 会被标注为不需要?
下面是一个非常简单的最小可复现示例。我使用 System.Diagnostics:
using System;
using System.Diagnostics;
class Program
{
static void Main()
{
string? myVariableString = Console.ReadLine();
int? myVariable;
try
{
myVariable = int.Parse(myVariableString!);
}
catch (FormatException)
{
myVariable = null;
}
Debug.Assert(myVariable.HasValue);
Console.WriteLine(myVariable!.Value);
}
}
我的理解是,Rider应在调试模式编译时将后缀运算符 ! 标记为冗余(它确实如此!它写着“空值警告抑制表达式是冗余的”)而在发布模式下则不应该,因为在启用优化时 Debug.Assert 将不会执行。然而,即使在发布模式,我也会看到以下警告
空值警告抑制表达式是冗余的
这是为什么?是什么确保编译器/静态分析器确信它是冗余的?
我正在运行.NET 10.0.104,并在Windows 11机器上使用JetBrains Rider 2026.1.0.1进行编码。
解决方案
免责声明!
- 这应该交给JetBrains Rider团队来处理
- Rider至少之前使用自己的一套语法树模型,并没有依赖Roslyn等,因此后续的一些推断可能完全错误。
我的猜测是,这种情况发生在绑定/流程分析阶段之后的降级/优化阶段进行的原因。基于 [.NET编译器平台("Roslyn")概览] 文章(两年来未更新):
Roslyn将 C#与 Visual Basic编译器的代码分析提供给你作为使用者,方法是提供一个镜像传统编译器流水线的API层。
该流水线的每个阶段现在都是一个独立的组件。首先是解析阶段,将源代码标记并解析为遵循语言语法的语法结构。其次是声明阶段,在该阶段对源代码及导入的元数据中的声明进行分析以形成命名符号。接下来是绑定阶段,在代码中的标识符与符号进行匹配。最后是发射阶段,将编译器构建的所有信息输出为一个程序集。
对应于这些阶段,每个阶段都暴露一个对象模型,允许在该阶段访问信息。解析阶段以语法树的形式暴露,声明阶段以分层符号表的形式暴露,绑定阶段则作为暴露编译器语义分析结果的模型,发射阶段作为生成IL字节码的API。
也可以在这里看到
请注意,你正在使用的是可空值类型,与可空引用类型略有不同,但如果切换到可空引用类型,你也会看到在发布模式下编译器也会对 Debug.Assert 发出警告:
MyClass? myVariable = null;
Debug.Assert(myVariable.HasValue);
Console.WriteLine(myVariable.Value);
class MyClass
{
public int Value { get; set; }
public bool HasValue { get; set; }
}
你可以在 @sharplab.io 自行测试
如你所见,并没有输出 Debug.Assert,但诊断只对包含它的那一行进行报告。
因此可以说,Rider的行为与编译器的行为是一致的。





