int? и string?
Переменная int всегда хранит число. Но бывает, что честного числа нет: пользователь не указал возраст, датчик не прислал температуру. Ноль здесь соврал бы: возраст 0 — тоже возраст.
Положить в int значение null нельзя. Значимый тип хранит само значение, а не ссылку, и строка int age = null; не скомпилируется: компилятор ответит ошибкой CS0037. С вашей структурой то же самое: строка Point p = null; не скомпилируется.
Представьте анкету, где рядом с полем «Возраст» стоит галочка «заполнено». В C# для этого есть типы с вопросительным знаком: int? хранит число и отметку о том, есть ли оно.
Тип int?
Типом, допускающим null, называют значимый тип со знаком ?: int?, double?, bool?. Полное имя int? — Nullable<int>. Это структура из двух частей: само значение и флаг, есть ли оно.
int? age = null;
Console.WriteLine(age.HasValue); // False
Console.WriteLine(age ?? -1); // -1
age = 30;
Console.WriteLine(age.HasValue); // True
Console.WriteLine(age.Value + 1); // 31Давайте разберём пример по строкам:
int? age = null;— поле пустое, галочки нет.age.HasValueпечатаетFalse: значения нет.age ?? -1подставляет-1. Оператор??работает сint?так же, как со ссылками.- После
age = 30значение есть, иHasValueпечатаетTrue. age.Valueчитает число, поэтомуage.Value + 1печатает31.
Здесь анкета и int? расходятся. На бумаге можно вписать число и забыть поставить галочку. В int? галочка ставится сама: присвоили число — HasValue стал true.
int? и int — разные типы. Строка int x = age; не скомпилируется (ошибка CS0266): в age может не оказаться числа. Число достают явно: через age ?? 0 или через age.Value, когда вы проверили HasValue.
?. даёт int?
У строки свойство Length — типа int. Но если строки нет, то нет и длины, поэтому word?.Length даёт int?:
string? word = null;
int? length = word?.Length;
Console.WriteLine(length.HasValue); // False
int safe = word?.Length ?? 0;
Console.WriteLine(safe); // 0word?.Length при null в word даёт пустой int?, и HasValue печатает False. Чтобы получить обычный int, добавляют ?? 0: тогда вместо пустого значения подставится 0.
Ссылочные типы и string?
Ссылочная переменная может хранить null всегда: так устроены ссылки. Поэтому у ссылочного типа знак ? значит другое — это подсказка компилятору:
string— здесьnullне ожидается;string?— здесь может оказатьсяnull.
В новых проектах и в программах, запущенных через dotnet run hello.cs, включена проверка, которую называют ссылочными типами, допускающими null. Компилятор по этим подсказкам следит, куда может попасть null, и предупреждает об опасных местах:
string? nick = null;
string name = nick; // ✗
Console.WriteLine(nick.Length); // ✗warning CS8600: Converting null literal or possible null value to non-nullable type.
warning CS8602: Dereference of a possibly null reference.Что означают два предупреждения:
CS8600— возможныйnullпопадает в переменную типа без?;CS8602— программа обращается к члену через переменную, в которой может бытьnull.
Это предупреждения, а не ошибки: программа компилируется и запускается. Но третья строка тут же останавливает её исключением NullReferenceException, о котором и предупреждал CS8602. Предупреждение пропадает, когда компилятор видит проверку: внутри if (nick != null) обращаться к nick.Length можно.
int? или string?
Этот вопрос часто задают на собеседовании: чем int? отличается от string?? Ответ из двух частей:
int?— отдельный типNullable<int>: структура со значением и флагомHasValue, которая есть и во время работы программы;string?— тот же типstringс пометкой для компилятора: при запуске разницы междуstringиstring?нет, а оnullпредупреждает только компилятор.
Частая ошибка
Value прочитали, не проверив, есть ли значение:
int? score = null;
Console.WriteLine(score.Value); // ✗Unhandled exception. System.InvalidOperationException: Nullable object must have a value.Компилятор предупреждает об этой строке (CS8629: Nullable value type may be null.), но программу компилирует. Когда программа запускается, Value у пустого int? останавливает её исключением InvalidOperationException. Перед Value проверьте HasValue или возьмите score ?? 0.
Главное
int?— значимый тип, который может хранитьnull:HasValueговорит, есть ли значение, аValueего читает.Valueу пустогоint?выбрасываетInvalidOperationException; безопасно читать через??.string?— пометка для компилятора: он выдаёт предупрежденияCS8600иCS8602, но программа компилируется.int?— отдельный типNullable<int>, аstringиstring?при запуске одинаковы.