JavaRush /Курсы /C# SELF /Nullable-типы для значимых типов

Nullable-типы для значимых типов

C# SELF
14 уровень , 0 лекция
Открыта

1. Введение

Давайте представим себе такую ситуацию. Вы храните данные о котах в коде. И вам хочется кроме, например, возраста кота, хранить еще состояния наподобие "нет данных", "возраст неизвестен". Можно было бы хранить 0, но нуль — это вполне себе валидный возраст.

int age = 0; // Возраст кота. Но что такое 0? Это котенок или "неизвестно"?

Проблема в том, что int принимает целые значения, а "нет данных" среди целых чисел не предусмотрено. Если же возраст — строка, там можно было бы присвоить null. Но с числами такой фокус не проходит:

int age = null; // Ошибка! Нельзя присвоить null переменной типа int

Это связано с тем, что значимые типы (struct-ы вроде int, double, DateTime, bool) всегда что-то хранят. У них нет "пустого" состояния (в отличие от ссылочных типов, где null — это всего лишь отсутствие объекта).

Пример из жизни:
Пытаясь обойти это ограничение, программисты иногда придумывают "особые" значения: например, -1 или int.MaxValue для "нет значения". Но это некрасиво, неудобно и опасно: легко перепутать реальное значение с заглушкой.

2. Nullable-тип: тип, у которого есть "null"

Идея

А что, если позволить обычным числам (и вообще всем значимым типам) хранить не только значение, но и null?
В C# это реализовано с помощью специального класса-обёртки Nullable<T>. Такой класс может содержать либо значение типа T, либо ничего (то есть null).

Синтаксис

int? age = null; // Переменная age может быть и числом, и null
Объявление nullable-типа с помощью ?

Компилятор C#, когда встречает такой код, фактически воспринимает его как:

Nullable<int> age = new Nullable<int>(); // Переменная age не содержит значения

Или, если нужно явно указать значение:

Nullable<int> age = new Nullable<int>(42); // age содержит число 42

Таким образом, знак вопроса превращает любой значимый тип в его nullable-версию:

  • int? (то же самое, что Nullable<int>)
  • double?
  • DateTime?
  • и любые другие структуры

Nullable-обёртки для самых популярных типовых структур

Обычный тип Nullable-тип Пример записи
int
int?
int? a = null;
double
double?
double? x = 3.14;
bool
bool?
bool? ok = null;
DateTime
DateTime?
DateTime? d = null;

Можно записать и длиннее, используя класс-обёртку:


Nullable<int> pages = null;
        
Длинная форма записи

Но писать int? — короче, а значит, круче.

3. Как использовать nullable-тип

Присваивание и проверка

Присваивание null работает так же, как и с ссылочными типами:

Объявление и присваивание

int? temperature = null;
temperature = 25;

Проверка на null

Как узнать, есть ли в нашем nullable-типе настоящее значение?

if (temperature != null)
{
    Console.WriteLine($"Температура: {temperature}");
}
else
{
    Console.WriteLine("Нет данных о температуре");
}

Свойства Nullable-типов: .HasValue и .Value

У nullable-типов есть два важных свойства:

  • .HasValue — возвращает true, если в переменной что-то лежит (не null).
  • .Value — само значение, если оно есть (иначе будет исключение).
int? temperature = null;
if (temperature.HasValue) //исключения не будет!
{
    Console.WriteLine($"Температура: {temperature.Value}°C");
}
else
{
    Console.WriteLine("Температура неизвестна");
}

Этот код будет работать: temperature не равно null, оно содержит null внутри. И вы всегда можете вызвать у Nullable типа свойство HasValue. А вот пытаться получить .Value, когда его нет — плохая идея, получите исключение InvalidOperationException. Проверяйте наличие значения заранее!

Сокращённый синтаксис вывода

Во многих случаях достаточно просто использовать переменную nullable-типа — и C# сам всё поймёт:

int? hours = null;
Console.WriteLine(hours); // ничего не выводит (просто пусто)
hours = 10;
Console.WriteLine(hours); // выводит 10

4. Операции и nullable-типы: арифметика, сравнения, преобразования

Операции: что происходит?

Если один из операндов — nullable, результат тоже nullable.
А если один из операндов равен null, результат тоже null.

int? a = 5;
int? b = null;
int? sum = a + b; // sum == null

int? c = 10;
int? d = 15;
int? total = c + d; // total == 25

Операции сравнения

Можно сравнивать nullable-типы с обычными числами:

int? score = null;
if (score > 0) Console.WriteLine("Отличный результат!");
else Console.WriteLine("Нет результата"); // Сработает эта ветка

Если score равен null, условие score > 0 будет false.

Явное и неявное преобразование

  • Из обычного типа в nullable: автоматически.
  • Из nullable в обычный: только если не null (иначе будет исключение).
int? x = 3;
int y = x.Value; // Ok, если x не null

// Неявно
int? z = y;

5. Nullable и методы: параметры и возвращаемые значения

Возвращение nullable-значений

Если метод не всегда может вернуть результат, используйте nullable-типы как возвращаемое значение:

// Метод ищет пользователя по логину и возвращает его возраст, если нашёл, иначе null
int? FindUserAge(string login)
{
    // ... тут логика поиска
    return null; // если не найден
}

Nullable-параметры

Можно принимать nullable-значения в параметры, чтобы явно дать понять: “может быть, а может и не быть”.

void PrintTemperature(int? temp)
{
    if (temp == null)
        Console.WriteLine("Температура неизвестна");
    else
        Console.WriteLine($"Температура: {temp} градусов");
}
2
Задача
C# SELF, 14 уровень, 0 лекция
Недоступна
Nullable-типы: объявление и проверка
Nullable-типы: объявление и проверка
2
Задача
C# SELF, 14 уровень, 0 лекция
Недоступна
Арифметика с nullable-типами
Арифметика с nullable-типами
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ