C# 可空性未正确推断

C# Nullability not deduced correctly

考虑:

#nullable enable

class Manager { 
  public int Age; 
}

class Archive {
  readonly Dictionary<string, Manager> Dict = new Dictionary<string, Manager>();

  public (bool ok, Manager? value) this[string key] {
    get {
      return Dict.TryGetValue(key, out var value) ? (true, value) : (false, null);
    }
  }
}

然后我尝试:

Archive archive = new Archive();
var (ok, john) = archive["John"];
if (!ok) return;
int age = john.Age; // <-- warning

我收到警告:

Warning CS8602 Dereference of a possibly null reference.

为什么?我预计在检查 !ok 之后,编译器会推断出 john 不为 null

我尝试的另一件事是:

public (bool ok, Manager value) this[string key] {
  get {
    return Dict.TryGetValue(key, out var value) ? (true, value) : default;
  }
}

(从管理器结果中删除了 ?,并将 (false, null) 替换为 default
我现在没有收到任何警告,但如果我删除 !ok.

的检查,我也不会收到任何警告

有什么方法可以实现我在这里想要的 - 一个警告 当且仅当 之前没有检查 !ok (那是我忘了检查为此)

谢谢

Why ? I expected that after checking for !ok the compiler will deduce that john is not null

这不起作用的原因有两个:

  1. 可空性分析一次只关注一种方法。

分析时:

Archive archive = new Archive();
var (ok, john) = archive["John"];
if (!ok) return;
int age = john.Age; // <-- warning

编译器看不到这个方法:

  public (bool ok, Manager? value) this[string key] {
    get {
      return Dict.TryGetValue(key, out var value) ? (true, value) : (false, null);
    }
  }

并告诉 valueok 为真时不为空。

  1. 可空性分析不跟踪布尔变量。

目前编译器不够智能,无法跟踪布尔变量的来源,并根据它们更新可空性。例如,以下不会发出警告:

M(string? str)
{
    if (string != null)
        Console.WriteLine(str.Length);
}

但是下面的等效代码可以:

M(string? str)
{
    var isNotNull = string != null;
    if (isNotNull)
        Console.WriteLine(str.Length);
}

Is there any way to achieve what I want here - a warning if and only if there was no previous check for !ok (that is I forgot to check for it)

恐怕不适用于元组。最好的方法是使用 out 参数,尽管这意味着您不能使用索引器:

public bool TryGetManager(string key, [NotNullWhen(true)] Manager? manager) 
    => Dict.TryGetValue(key, out manager);