C# Типы, допускающие значение NULL
В C#,int、float、bool、DateTimeи т. д. являютсязначимыми типами, они должны иметь значение по умолчанию и не могут бытьnull:
int a = null; // 编译错误:无法将 null 转换为 int
Типы, допускающие значение NULL,решают эту проблему — они дают значимым типам дополнительное состояние «нет значения» (т. е.,null), что очень полезно в сценариях, где данные могут отсутствовать, например в полях базы данных и возвращаемых значениях API.
Объявление очень простое: добавьте?:
int? a = null; // 合法 int? b = 42; // 合法
здесьint?ДаNullable<int>синтаксический сахар (сокращённая форма), эти два варианта полностью эквивалентны:
Nullable<int> a = null; // 完整写法 int? a = null; // 简写,效果相同
На следующей диаграмме показано, как типы, допускающие значение NULL, работают в памяти — они используют дополнительный логический флаг, чтобы фиксировать «существует ли значение»:
Разница между одинарным вопросительным знаком ? и двойным вопросительным знаком ??
Два оператора, связанных с типами, допускающими значение null в C#, часто используются вместе, но имеют совершенно разный смысл:
| Операторы | Name | Назначение | Пример |
|---|---|---|---|
? |
Модификатор типа, допускающего значение null | Делает типы значений способными бытьnull |
int? i = 3;ЭквивалентноNullable<int> i = new Nullable<int>(3); |
?? |
Оператор объединения с null | Когда переменнаяnullпредоставить значение по умолчанию, когда |
int result = i ?? 0; |
Простое сравнение:
int i; // 普通值类型,默认值为 0,永远不能是 null int? ii; // 可空类型,默认值为 null
Объявление и присваивание типов, допускающих значение null
Синтаксис объявления
<data_type>? <variable_name> = null;
Например:
int? age = null; double? temperature = 36.6; bool? isActive = new bool?(); // 显式构造,默认为 null DateTime? birthday = null;
Nullable<T>может представлять обычный диапазон своего базового типа значения, плюсnullЗначение. Например,Nullable<int>может быть-2,147,483,648в2,147,483,647любое целое число между ними, илиnull。
Полный пример
Пример
namespace NullableDemo
{
class Program
{
static void Main(string[] args)
{
// Объявите переменные разных типов, допускающие значение null
int? num1 = null;
int? num2 = 45;
double? num3 = new double?(); // null
double? num4 = 3.14157;
bool? boolVal = new bool?(); // null
// Отобразить значение (null будет отображаться как пустая строка)
Console.WriteLine($"num1 = {num1 ?? 0}"); // 0
Console.WriteLine($"num2 = {num2 ?? 0}"); // 45
Console.WriteLine($"num3 = {num3 ?? 0.0}"); // 0
Console.WriteLine($"num4 = {num4 ?? 0.0}"); // 3.14157
Console.WriteLine($"boolVal = {boolVal}"); // (пусто)
}
}
}
Результат вывода:
num1 = 0 num2 = 45 num3 = 0 num4 = 3.14157 boolVal =
Оператор объединения с null (??)
??оператор используется для предоставлениярезервного значения по умолчанию. Когда левая часть неnullкогда оно не равно null, возвращает левое значение; в противном случае возвращает правое значение:
<表达式1> ?? <表达式2>
- if
<表达式1>неnull, возвращает<表达式1>; - иначе возвращает
<表达式2>。
Начиная с C# 8.0, также можно использовать??=(оператор присваивания объединения с null), только когда переменнаяnullПрисваивать только когда:
int? x = null; x ??= 10; // x 为 null,赋值为 10 x ??= 20; // x 已有值 10,不再赋值
Пример
namespace NullableDemo
{
class Program
{
static void Main(string[] args)
{
double? num1 = null;
double? num2 = 3.14157;
// ?? предоставляет значение по умолчанию
double result1 = num1 ?? 5.34; // num1 равен null → возвращает 5.34
double result2 = num2 ?? 5.34; // num2 имеет значение → возвращает 3.14157
Console.WriteLine($"num1 ?? 5.34 = {result1}"); // 5.34
Console.WriteLine($"num2 ?? 5.34 = {result2}"); // 3.14157
// Цепочное использование: предоставить несколько альтернативных значений
int? a = null;
int? b = null;
int? c = 42;
int value = a ?? b ?? c ?? 0; // Пробуем по порядку, в итоге получаем 42
Console.WriteLine($"a ?? b ?? c ?? 0 = {value}"); // 42
}
}
}
Результат вывода:
num1 ?? 5.34 = 5.34 num2 ?? 5.34 = 3.14157 a ?? b ?? c ?? 0 = 42
Общие свойства и методы типов, допускающих значение null
| член | Описание | Пример |
|---|---|---|
.HasValue |
Проверить, имеет ли переменная значение (возвращаетbool) |
if (num.HasValue) { ... } |
.Value |
Получить фактическое значение (если оноnullВызовет исключениеInvalidOperationException) |
int x = num.Value; |
.GetValueOrDefault() |
Безопасно получить значение; если оноnullВернуть значение по умолчанию для типа (например,0) |
num.GetValueOrDefault() |
.GetValueOrDefault(T) |
Безопасно получить значение; если оноnullВернуть указанное значение по умолчанию |
num.GetValueOrDefault(100) |
?? |
Оператор объединения с null (синтаксический сахар, эквивалентенGetValueOrDefault) |
int result = num ?? 100; |
Пример
namespace NullableDemo
{
class Program
{
static void Main(string[] args)
{
int? num = null;
// HasValue + Value (традиционный подход)
if (num.HasValue)
Console.WriteLine($"Value: {num.Value}");
else
Console.WriteLine("num has no value");
// Безопасно получить значение по умолчанию
Console.WriteLine(num.GetValueOrDefault()); // 0
Console.WriteLine(num.GetValueOrDefault(99)); // 99
Console.WriteLine(num ?? 99); // 99 (эквивалентная запись)
// ⚠ Примечание: прямое обращение к .Value вызовет исключение
// int x = num.Value; // InvalidOperationException!
}
}
}
Практические сценарии применения
Nullable-типы очень распространены в реальной разработке, особенно при работе с потенциально отсутствующими данными:
Сопоставление полей базы данных
Поля в базе данных могут бытьNULL, nullable-типы могут точно соответствовать:
| ID пользователя | Age | Активен ли |
|---|---|---|
| 1 | 28 | истина |
| 2 | null | false |
int? age = GetUserAgeFromDB(userId: 2);
// 安全处理 null
string display = age.HasValue
? $"用户年龄:{age.Value}"
: "年龄未知";
// 或者更简洁的写法
string display2 = $"用户年龄:{age ?? 0}";
Необязательные параметры и конфигурация
// 配置项可能未设置
int? timeout = GetConfigValue("timeout");
int actualTimeout = timeout ?? 30; // 未设置时使用默认值 30 秒
Цепочки проверок на null (C# 6.0+)
// 使用 ?. 运算符安全访问可能为 null 的对象 string? name = user?.Address?.City?.Name; // 如果 user、Address、City 中任何一个为 null,结果就是 null,不会抛异常
Итог
| Особенности | Пример | Описание |
|---|---|---|
| Определение nullable-типов | int? x = null; |
ЭквивалентноNullable<int> |
| Определение наличия значения | x.HasValue |
returntrueилиfalse |
| Получить значение (unsafe) | x.Value |
Если он равенnullВызовет исключениеInvalidOperationException |
| Получить значение по умолчанию | x ?? 0 |
Если он равенnullВернуть значение правой части |
| Получить указанное значение по умолчанию | x.GetValueOrDefault(10) |
Если он равенnullВернуть 10 |
| Присваивание объединения с null | x ??= 10 |
Только когдаxявляетсяnullприсваивать, когда (C# 8.0+) |
| Доступ с безопасной навигацией | user?.Name |
ifuserявляетсяnullзатем вернутьnull(C# 6.0+) |
«Ссылочные типы, допускающие значение null» в C# 8.0
Начиная с C# 8.0, ... был введён.Ссылочные типы, допускающие значение nullон и тот, что представлен в этой статьеТипы значений, допускающие значение nullпредставляют собой два разных механизма:
| Пункт сравнения | Типы значений, допускающие значение NULL | Ссылочные типы, допускающие значение NULL |
|---|---|---|
| Пример | int? |
string? |
| Целевой объект | Тип значения (int、bool、structи т.д.) |
Ссылочные типы (string、classмассивы и т. д.) |
| Подход к реализации | Во время выполнения с помощьюNullable<T>Реализация структуры |
Статический анализ компилятора не изменяет поведение во время выполнения |
| Состояние по умолчанию | существовало всегда (начиная с C# 1.0) | Необходимо включить в проекте<Nullable>enable</Nullable> |
| проверка на null | использовать.HasValueПринятие решений |
Компилятор выдаёт предупреждения (не ошибки) |
Другие расширенияСсылочные типы, допускающие значение null, — это инструмент проверки безопасности на этапе компиляции: он не изменяет поведение программы во время выполнения, но там, где код может вызвать
NullReferenceExceptionпредупреждения компилятора, которые выдаются и помогают выявить потенциальные проблемы на этапе разработки.