C# Nullable Types
In C#,int、float、bool、DateTimeetc. arevalue type, they must have a value by default and cannot benull:
int a = null; // 编译错误:无法将 null 转换为 int
Nullable Typessolves this problem—it gives value types an additional "no value" state (i.e.,null), which is very useful in scenarios where data may be missing, such as database fields and API return values.
The declaration is very simple: add a?:
int? a = null; // 合法 int? b = 42; // 合法
hereint?YesNullable<int>syntactic sugar (shorthand form), the two are completely equivalent:
Nullable<int> a = null; // 完整写法 int? a = null; // 简写,效果相同
The following diagram shows how nullable types work in memory—they use an extra Boolean flag to record "whether a value exists":
The difference between the single question mark ? and the double question mark ??
Two operators related to nullable types in C# are often used together, but have completely different meanings:
| Operators | Name | Purpose | Example |
|---|---|---|---|
? |
Nullable type modifier | Makes value types able to benull |
int? i = 3;Equivalent toNullable<int> i = new Nullable<int>(3); |
?? |
Null-Coalescing Operator | When the variable isnullprovide a default value when |
int result = i ?? 0; |
A simple comparison:
int i; // 普通值类型,默认值为 0,永远不能是 null int? ii; // 可空类型,默认值为 null
Declaration and assignment of nullable types
Declaration syntax
<data_type>? <variable_name> = null;
For example:
int? age = null; double? temperature = 36.6; bool? isActive = new bool?(); // 显式构造,默认为 null DateTime? birthday = null;
Nullable<T>can represent the normal range of its underlying value type, plus anullValue. For example,Nullable<int>can be-2,147,483,648to2,147,483,647any integer in between, ornull。
Complete example
Example
namespace NullableDemo
{
class Program
{
static void Main(string[] args)
{
// Declare nullable variables of different types
int? num1 = null;
int? num2 = 45;
double? num3 = new double?(); // null
double? num4 = 3.14157;
bool? boolVal = new bool?(); // null
// Display value (null will be displayed as an empty string)
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}"); // (empty)
}
}
}
Output result:
num1 = 0 num2 = 45 num3 = 0 num4 = 3.14157 boolVal =
Null coalescing operator (??)
??operator is used to provide aFallback default value. When the left side is notnullwhen it is not null, returns the left value; otherwise, returns the right value:
<表达式1> ?? <表达式2>
- if
<表达式1>is notnull, return<表达式1>; - otherwise returns
<表达式2>。
As of C# 8.0, you can also use??=(null-coalescing assignment operator), only when the variable isnullOnly assign when:
int? x = null; x ??= 10; // x 为 null,赋值为 10 x ??= 20; // x 已有值 10,不再赋值
Example
namespace NullableDemo
{
class Program
{
static void Main(string[] args)
{
double? num1 = null;
double? num2 = 3.14157;
// ?? provides a default value
double result1 = num1 ?? 5.34; // num1 is null → returns 5.34
double result2 = num2 ?? 5.34; // num2 has a value → returns 3.14157
Console.WriteLine($"num1 ?? 5.34 = {result1}"); // 5.34
Console.WriteLine($"num2 ?? 5.34 = {result2}"); // 3.14157
// Chained usage: provide multiple alternative values
int? a = null;
int? b = null;
int? c = 42;
int value = a ?? b ?? c ?? 0; // Try in sequence, eventually get 42
Console.WriteLine($"a ?? b ?? c ?? 0 = {value}"); // 42
}
}
}
Output result:
num1 ?? 5.34 = 5.34 num2 ?? 5.34 = 3.14157 a ?? b ?? c ?? 0 = 42
Common properties and methods of nullable types
| member | Description | Example |
|---|---|---|
.HasValue |
Check whether the variable has a value (returnsbool) |
if (num.HasValue) { ... } |
.Value |
Get the actual value (if it isnullWill throwInvalidOperationException) |
int x = num.Value; |
.GetValueOrDefault() |
Safely get the value; if it isnullReturn the default value of the type (such as0) |
num.GetValueOrDefault() |
.GetValueOrDefault(T) |
Safely get the value; if it isnullReturn the specified default value |
num.GetValueOrDefault(100) |
?? |
Null coalescing operator (syntactic sugar, equivalent toGetValueOrDefault) |
int result = num ?? 100; |
Example
namespace NullableDemo
{
class Program
{
static void Main(string[] args)
{
int? num = null;
// HasValue + Value (traditional approach)
if (num.HasValue)
Console.WriteLine($"Value: {num.Value}");
else
Console.WriteLine("num has no value");
// Safely get the default value
Console.WriteLine(num.GetValueOrDefault()); // 0
Console.WriteLine(num.GetValueOrDefault(99)); // 99
Console.WriteLine(num ?? 99); // 99 (equivalent notation)
// ⚠ Note: Directly accessing .Value will throw an exception
// int x = num.Value; // InvalidOperationException!
}
}
}
Practical application scenarios
Nullable types are very common in real-world development, especially when dealing with potentially missing data:
Database field mapping
Fields in the database are allowed to beNULL, nullable types can precisely correspond:
| User ID | Age | Whether active |
|---|---|---|
| 1 | 28 | true |
| 2 | null | false |
int? age = GetUserAgeFromDB(userId: 2);
// 安全处理 null
string display = age.HasValue
? $"用户年龄:{age.Value}"
: "年龄未知";
// 或者更简洁的写法
string display2 = $"用户年龄:{age ?? 0}";
Optional parameters and configuration
// 配置项可能未设置
int? timeout = GetConfigValue("timeout");
int actualTimeout = timeout ?? 30; // 未设置时使用默认值 30 秒
Chained null checks (C# 6.0+)
// 使用 ?. 运算符安全访问可能为 null 的对象 string? name = user?.Address?.City?.Name; // 如果 user、Address、City 中任何一个为 null,结果就是 null,不会抛异常
Summary
| Features | Example | Description |
|---|---|---|
| Defining nullable types | int? x = null; |
Equivalent toNullable<int> |
| Determine whether a value exists | x.HasValue |
returntrueorfalse |
| Get the value (unsafe) | x.Value |
If it isnullWill throwInvalidOperationException |
| Get default value | x ?? 0 |
If it isnullReturn the right-side value |
| Get the specified default value | x.GetValueOrDefault(10) |
If it isnullReturn 10 |
| Null-coalescing assignment | x ??= 10 |
Only whenxisnullassign when (C# 8.0+) |
| Safe navigation access | user?.Name |
ifuserisnullthen returnnull(C# 6.0+) |
C# 8.0's "nullable reference types"
Starting with C# 8.0, ... was introduced.Nullable Reference Typesit and the one introduced in this articleNullable value typesare two different mechanisms:
| Comparison Item | Nullable value types | Nullable reference types |
|---|---|---|
| Example | int? |
string? |
| Target object | Value type (int、bool、structetc.) |
Reference types (string、classarrays, etc.) |
| Implementation Approach | At runtime viaNullable<T>Struct implementation |
Compiler static analysis, does not change runtime behavior |
| Default state | has always existed (since C# 1.0) | Need to be enabled in the project<Nullable>enable</Nullable> |
| null check | use.HasValueDecision Making |
The compiler issues warnings (not errors) |
other extensionsNullable reference types are a compile-time safety checking tool—it does not change the runtime behavior of the program, but where code may produce
NullReferenceExceptioncompiler warnings are issued, helping you discover potential issues during the development stage.