Если мы собираемся использовать переменную или параметр, которые допускают значение null, то есть представляют nullable-тип (не важно значимый или ссылочный), то, чтобы избежать возникновения NullReferenceException, мы можем проверить на null:
void PrintUpper(string? text) { if (text!=null) { Console.WriteLine(text.ToUpper()); } }
В данном случае если параметр text не равен null, то вызываем у строки метод ToUpper()
, который переводит символы строки в верхний регистр.
Кроме того, с помощью оператора is мы можем проверить значение объекта:
объект is значение
Если объект слева от оператора is имеет значение справа от оператора. тогда оператор is возвращает true
, иначе
возвращается false
Например, проверка параметра/переменной на значение null:
void PrintUpper(string? text) { if (text is null) return; Console.WriteLine(text.ToUpper()); }
Или, наоборот, с помощью is not можно проверить отсутствие значения:
void PrintUpper(string? text) { if (text is not null) Console.WriteLine(text.ToUpper()); }
Также можно проверить на соответствие типу, значение которого мы собираемся использовать:
void PrintUpper(string? text) { if (text is string) Console.WriteLine(text.ToUpper()); else Console.WriteLine("NULL"); }
Подобные проверки еще называются null guard или условно говоря "защита от null".
Оператор ?? называется оператором null-объединения. Он применяется для установки значений по умолчанию для типов, которые допускают значение null:
левый_операнд ?? правый_операнд
Оператор ?? возвращает левый операнд, если этот операнд не равен null
.
Иначе возвращается правый операнд. При этом левый операнд должен принимать null. Посмотрим на примере:
string? text = null; string name = text ?? "Tom"; // равно Tom, так как text равен null Console.WriteLine(name); // Tom int? id = 200; int personid = id ?? 1; // равно 200, так как id не равен null Console.WriteLine(personid); // 200
Но мы не можем написать следующим образом:
int x = 44; int y = x ?? 100;
Здесь переменная x представляет значимый тип int и не может принимать значение null, поэтому в качестве левого операнда в операции ?? она использоваться не может.
Также можно использовать производный оператора ??=
string? text = null; text ??= "Sam"; // аналогично // text = text ?? "Sam"; Console.WriteLine(text); // Sam int? id = 100; id ??= 1; // аналогично //id = id ?? 1; Console.WriteLine(id); // 100
Иногда при работе с объектами, которые принимают значение null, мы можем столкнуться с ошибкой: мы пытаемся обратиться к объекту, а этот объект равен null. Например, пусть у нас есть следующая система классов:
class Person { public Company? Company { get; set; } // место работы } class Company { public string? WebSite { get; set; } // веб-сайт компании }
Объект Person представляет человека. Его свойство Company представляет компанию, где человек работает. Но человек может не работать, поэтому свойство Company имеет
тип Company?
, то есть может иметь значение null.
Класс Company в свою очередь содержит свойство WebSite, которое представляет веб-сайт компании. Но у компании может и не быть собственного веб-сайта.
Поэтому это свойство имеет тип string?
, то есть также допускает значение null.
Допустим, нам надо вывести на консоль заглавными буквами веб-сайт компании, где работает человек (если он, конечно, работает и если у компании, где он работает, есть сайт). На первый взгляд мы можем написать следующую конструкцию:
void PrintWebSite(Person? person) { if (person != null) { if(person.Company != null) { if(person.Company.WebSite != null) { Console.WriteLine(person.Company.WebSite.ToUpper()); } } } } class Person { public Company? Company { get; set; } // место работы } class Company { public string? WebSite { get; set; } // веб-сайт компании }
В методе PrintWebSite()
принимаем объект Person? и, чтобы избежать исключения NullReferenceException, последовательно проверяем все используемые значения на null, чтобы в конце с помощью метода
ToUpper()
вывести заглавными буквами название сайта.
Хоть это и рабочий способ, но для простого вывода строки получается многоэтажная конструкция, но на самом деле ее можно сократить:
void PrintWebSite(Person? person) { if (person != null && person.Company != null && person.Company.WebSite != null) { Console.WriteLine(person.Company.WebSite.ToUpper()); } }
Конструкция намного проще, но все равно получается довольно большой. И чтобы ее упростить, в C# есть оператор условного null (Null-Conditional Operator) - оператор ?.:
объект?.компонент
Если объект не равен null, то происходит обращение к компоненту объекта - полю, свойству, методу. Если объект представляет значение null, обращение к компаненту метода не происходит.
Применим данный оператор, изменив предыдущий пример:
void PrintWebSite(Person? person) { Console.WriteLine(person?.Company?.WebSite?.ToUpper()); }
Таким образом, если person не равен null, то происходит обращение к его свойству Company. Если свойство Company не равно null, то идет обрашение к свойству
WebSite объекта Company. Если свойство WebSite не равно null, то идет обращение к методу ToUpper()
.