Проблемы с типами, допускающими значение NULL, в C# 9
Рассмотрим следующий код (VS 16.8.0 Preview 2.1 C# 9.0 preview):
#nullable enable
using System.Collections.Generic;
class Archive<T> where T : notnull
{
readonly Dictionary<string, T> Dict = new();
public T? GetAt(string key)
{
return Dict.TryGetValue(key, out var value) ? value : default;
}
}
class Manager
{
public int Age { get; set; }
}
class Main34
{
long F3()
{
Archive<long> a = new();
var johnAge = a.GetAt("john");
if (johnAge is null) return -1; // Error CS0037 Cannot convert null to 'long' because it is a non - nullable value type
return johnAge;
}
long F4()
{
Archive<Manager> a = new();
var johnAge = a.GetAt("john");
//if (johnAge is null) return -1;
return johnAge.Age; // Correct ! warning "Derefrencing of a possibly null reference" will be removed if line above unremarked
}
}
Мне сложно понять / исправить ошибки в F3. Похоже, компилятор считает, что johnAge есть
long
не
long?
(как я подтвердил, наведя на него курсор в VS), несмотря на возвращение
Archive<T>.GetAt
будучи
T?
Есть ли способ создать общий архив, который будет делать то, что я хочу (метод GetAt, который возвращает значение Nullable, даже если T является базовым типом, не допускающим значения NULL, т.е. long)?
1 ответ
По сути, это сводится к тому, что типы значений, допускающие значение NULL, и ссылочные типы, допускающие значение NULL, очень, очень разные. CLR знает о типах значений, допускающих значение NULL, но, что касается CLR, ссылочные типы, допускающие значение NULL, являются просто "нормальным ссылочным типом с атрибутом, который сообщает компилятору, следует ли считать его допускающим значение NULL".
когда
T
имеет
notnull
ограничение, тип
T?
просто компилируется в
T
в Ил. Он должен - он не может компилироваться в
Nullable<T>
, потому как
Nullable<T>
ограничения
T
быть типом значения.
Так что для
Archive<long>
, то
GetAt
метод вернет 0L, если ключ не найден в словаре - он не будет (и не может) вернуть нулевое значение
Nullable<long>
, это то, что ваш код в
F3
эффективно ожидает.
Вся функция "ссылочных типов, допускающих значение NULL" страдает от попытки добавить "видимость" поддержки значений NULL в системе типов, в которой ее в принципе нет. Я уверен, что если бы новая среда выполнения и язык разрабатывались вместе с нуля, они попытались бы объединить это более тесно. Как бы то ни было, я считаю, что эта функция по-прежнему имеет большую ценность, но определенно делает вещи действительно сложными, когда дело доходит до дженериков.