Типы значений, допускающие значение NULL: основы C#

Подсказка

Эта статья является частью раздела "Основы" для разработчиков , которые знают по крайней мере один язык программирования и учат C#. Если вы не знакомы с программированием, сначала начните с учебных пособий по началу работы. Дополнительные сведения см. в разделе "Типы значений, допускающие значение NULL" в справочнике по языку.

Тип T?, представляет все значения базового типа Tзначения, а также дополнительное null значение. Переменная типа int? может содержать любое целое число или null, чтобы обозначить "нет значения".

Такие типы значений, как int, boolи DateTime не могут храниться null по умолчанию. Это поведение эффективно и предотвращает множество ошибок. Однако это ограничение создает проблему, когда значение может быть действительно отсутствует. Распространенный сценарий считывается из базы данных: целочисленный столбец может содержать число или не содержать значения вообще (NULL в SQL). Простое int не может выразить это отсутствие, но int? может.

Объявление типа значения, принимающего NULL

Добавьте ? к любому типу значения, чтобы сделать его пустым:

int?    age      = null;    // integer with no value yet
double? price    = 9.99;    // nullable double with a value
bool?   isActive = null;    // boolean with no value

age = 30;                   // assign a value later

int?[] scores = [100, null, 85, null, 72]; // array with absent entries

Значение по умолчанию для типа значения, допускающего NULL, — это null, а не значение по умолчанию базового типа.

Проверка наличия значения

Рекомендуемый способ проверки типа значения, допускающего значение NULL, и извлечения его значения - с шаблоном типа:

int? temperature = 72;

if (temperature is int degrees)
{
    Console.WriteLine($"Temperature is {degrees}°F.");
}
else
{
    Console.WriteLine("Temperature is not recorded.");
}
// Output: Temperature is 72°F.

Шаблон is int degrees совпадает только тогда, когда temperature не равно null, и при этом связывает его с degrees. Вы получаете как проверку NULL, так и извлечение значений на одном шаге.

В качестве альтернативы, используйте свойства HasValue и Value.

int? count = 42;

if (count.HasValue)
{
    Console.WriteLine($"Count is {count.Value}.");
}
else
{
    Console.WriteLine("Count has no value.");
}
// Output: Count is 42.

Предпочитайте шаблон is T value для нового кода. В ней представлена новая переменная, не допускающая значения NULL, ограниченная соответствующей ветвью, что делает намерение более понятным и устраняет любой соблазн случайно использовать Value за пределами проверки NULL, где он вызовет InvalidOperationExceptionисключение.

Вы также можете сравнить напрямую с null:

int? quantity = null;

if (quantity != null)
{
    Console.WriteLine($"Quantity: {quantity.Value}");
}
else
{
    Console.WriteLine("Quantity is not set.");
}
// Output: Quantity is not set.

Получение значения с резервным вариантом

Если требуется значение, не допускающее NULL, из значения, допускающего NULL, используйте GetValueOrDefault или null-объединяющий оператор ??.

int? rating = null;

int result1 = rating.GetValueOrDefault();    // 0 (default for int)
int result2 = rating.GetValueOrDefault(-1);  // -1 (specified fallback)

Console.WriteLine(result1); // 0
Console.WriteLine(result2); // -1

rating = 5;
int result3 = rating.GetValueOrDefault(-1);  // 5 (actual value)
Console.WriteLine(result3); // 5

Оператор ?? часто выглядит более аккуратно в строку.

int? priority = null;

int effective = priority ?? 0;  // 0 because priority is null
Console.WriteLine(effective);   // 0

priority  = 3;
effective = priority ?? 0;      // 3 because priority has a value
Console.WriteLine(effective);   // 3

Оба подхода возвращают фактическое значение, когда оно присутствует, и запасное значение, которое вы указали, когда его нет.

Арифметика с типами значений, допускающими значение NULL

Арифметические операторы и операторы сравнения для типов значений, допускающих значение NULL, повышаются: если один из операндов null, результат будет null, а не ошибка.

int? a = 10;
int? b = 20;
int? c = null;

int? sum     = a + b;   // both non-null: result is 30
int? product = a * c;   // one operand is null: result is null

Console.WriteLine(sum);               // 30
Console.WriteLine(product.HasValue);  // False — null propagates through arithmetic

Значение NULL распространяется по умолчанию с помощью арифметики. Чтобы предотвратить каскадный результат null, извлеките значение с ?? или GetValueOrDefault перед его использованием в вычислении.

См. также