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&lt;int&gt; 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":

Memory layout of Nullable<T> int (ordinary value type) Value: 42 4 bytes, always has a value Package Nullable<int> (nullable type) HasValue Value 1 byte 4 bytes 5+ bytes total int? x = 42; true ✓ 42 HasValue = true,Value = 42 int? y = null; false ✗ None HasValue = false, accessing Value will throw an exception.

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

using System;

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

using System;

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

using System;

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)

            // &#x26a0; 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)

Nullable reference types are a compile-time safety checking tool—it does not change the runtime behavior of the program, but where code may produceNullReferenceExceptioncompiler warnings are issued, helping you discover potential issues during the development stage.

other extensions