检查 "possibly null" 引用的正确方法是什么?

What is the correct way to check for "possibly null" references?

我有这段相关代码:

if (boardMap[i,j] != null) {
    Console.Write(boardMap[i,j].color.ToString() + " " + boardMap[i,j].type.ToString());    
}
else {
    Console.Write("X");
}

boardMap 包含潜在的空值,这就是我实施 if 语句检查 boardMap[i,j] 项是否为空的原因。但是,我在第 2 行收到一条警告,告诉我 boardMap[i,j] 可能为空。
我该如何解决这个问题 -> 像这样进行空值检查的正确方法是什么?

(请注意,我是 dotnet 和 C# 的初学者)

目前编译器不能很好地处理这种情况(和数组 in general). To help compiler determine the null-state of the item you can use pattern matching with empty property pattern:

if (boardMap[i,j] is {} map) 
{
    Console.Write(map.color.ToString() + " " + map.type.ToString());    
}
else
{
    Console.Write("X");
}

或者引入一个变量:

var map = boardMap[i,j];
if (map != null) 
{
   ...
}

从 C# 8.0 及更高版本开始,您可以使用 null-forgiving operator。只是追加!在任何可能为空之后。它基本上告诉编译器它应该关闭这种情况的可能为空的警告。

if (boardMap[i,j] != null) {
    Console.Write(boardMap[i,j]!.color.ToString() + " " + boardMap[i,j]!.type.ToString());    
}
else {
    Console.Write("X");
}

尽管您 if,编译器无法推断 boardMap[i,j] 不会为 null,因此会发出此警告。

编辑 @GuruStron 指出这种警告仅在 C#8 之后发出。我找不到这方面的参考,但我倾向于相信它。这意味着如果有人有这个问题,他至少使用 C#8。

您通常可以根据需要以不同的方式处理此类问题。

在多线程应用程序中,boardMap[i,j] 可能会在您检查它不为 null 后 10 纳秒设置为 null。这就是(可能)编译器现在抱怨的原因。 当然这取决于你的代码。 如果您确定该数组仅由一个线程处理,则您的 null 检查是安全的,您可以忽略此处的警告。

在多线程场景(无锁定)中保护您的代码的一种简单方法是将数组值分配给局部变量。并对该变量进行空检查和 Console.Write。那么空检查是安全的。参见@GuruStron post