Обнуляемые ссылочные типы с универсальным типом возврата

Я немного поиграюсь с новой функцией С# 8 для обнуляемых ссылочных типов, и во время рефакторинга моего кода я наткнулся на этот (упрощенный) метод:

public T Get<T>(string key)
{
    var wrapper = cacheService.Get(key);
    return wrapper.HasValue ? Deserialize<T>(wrapper) : default;
}

Теперь это выдает предупреждение " Possible null reference return что логично, поскольку default(T) будет давать ноль для всех ссылочных типов. Сначала я думал, что я изменил бы это к следующему:

public T? Get<T>(string key)

Но это не может быть сделано. В нем говорится, что я либо должен добавить общее ограничение, where T: class или where T: struct. Но это не вариант, так как это может быть и другое (я могу хранить int или int? Или экземпляр FooBar или что-то еще в кэше). Я также читал о предполагаемом новом родовом ограничении, where class? но это не похоже на работу.

Единственное простое решение, о котором я могу подумать, - это изменение оператора return с помощью оператора null forgiving:

return wrapper.HasValue ? Deserialize<T>(wrapper) : default!;

Но это кажется неправильным, поскольку оно определенно может быть нулевым, поэтому я вру здесь компилятору :-)

Как я могу это исправить? Я что-то упускаю здесь совершенно очевидно?

Ответ 1

Я думаю, что по default! это лучшее, что вы можете сделать на данный момент.

Причина, почему public T? Get<T>(string key) public T? Get<T>(string key) не работает, потому что ссылочные типы, допускающие значение NULL, сильно отличаются от типов значений, которые могут иметь значение NULL.

Обнуляемые ссылочные типы - это просто время компиляции. Маленькие вопросительные и восклицательные знаки используются только компилятором для проверки возможных нулей. Для глаз времени выполнения, string? и string точно такие же.

Типы значений Nullable, с другой стороны, являются синтаксическим сахаром для Nullable<T>. Когда компилятор компилирует ваш метод, он должен определить тип возврата вашего метода. Если T является ссылочным типом, ваш метод будет иметь возвращаемый тип T Если T является типом значения, ваш метод будет иметь возвращаемый тип Nullable<T>. Но компилятор не знает, как с этим справиться, когда T может быть и тем, и другим. Конечно, нельзя сказать, что "тип возвращаемого значения - T если T - ссылочный тип, и это Nullable<T> если T - ссылочный тип". потому что CLR не поймет этого. Метод должен иметь только один тип возвращаемого значения.

Другими словами, говоря, что вы хотите вернуть T? все равно что сказать, что вы хотите вернуть T когда T является ссылочным типом, и вернуть Nullable<T> когда T является типом значения. Это не похоже на допустимый тип возвращаемого значения для метода, не так ли?

В качестве действительно плохого обходного пути вы могли бы объявить два метода с разными именами - у одного T ограничено типами значений, а у другого T ограничено ссылочными типами.