C# Nullability не выводится правильно
Рассматривать:
#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
Я получаю предупреждение:
Предупреждение CS8602 Разыменование возможно нулевой ссылки.
Почему? Я ожидал, что после проверки
!ok
компилятор выведет, что
john
не ноль
Еще я попробовал:
public (bool ok, Manager value) this[string key] {
get {
return Dict.TryGetValue(key, out var value) ? (true, value) : default;
}
}
(удалено
?
из Диспетчера результат и заменен
(false, null)
с участием
default
)
Теперь я не получаю предупреждения, но я также не получаю предупреждения, если снимаю чек для
!ok
.
Есть ли способ достичь того, что я хочу здесь - предупреждение, если и только если не было предыдущей проверки для
!ok
(то есть я забыл это проверить)
благодаря
1 ответ
Почему? Я ожидал, что после проверки на! Ok компилятор определит, что john не равен нулю
Это не работает по двум причинам:
- Анализ допустимости NULL рассматривает только один метод за раз.
При анализе:
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);
}
}
и скажи это
value
не равно нулю, когда
ok
правда.
- Анализ допустимости пустых значений не отслеживает логические переменные.
На данный момент компилятор недостаточно умен, чтобы отслеживать, откуда булевы переменные, и обновлять на их основе допустимость значений NULL. Например, следующее не предупреждает:
M(string? str)
{
if (string != null)
Console.WriteLine(str.Length);
}
But the following equivalent code does:
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)
Not with tuples I'm afraid. The best way is using out parameters, although it will mean you can't use an indexer:
public bool TryGetManager(string key, [NotNullWhen(true)] Manager? manager)
=> Dict.TryGetValue(key, out manager);