C# nullable types — int?, Nullable<T>, null handling patterns

Value types like int, bool, and DateTime can't normally be null — they always hold a value. Nullable value types (written as int?, bool?, DateTime?) wrap a value type in Nullable<T>, adding a null state to represent "no value."

This is essential when working with databases (where columns can be NULL), optional fields, or any scenario where the absence of a value is meaningful.

C# Example Code
int   a = null;  // ❌ compile error — int can't be null
int?  b = null;  // ✅ nullable int — has no value
int?  c = 42;    // ✅ nullable int — has value 42

HasValue and Value

C# Example Code
int? age = null;

Console.WriteLine(age.HasValue); // False
// age.Value would throw InvalidOperationException — never call it without checking!

age = 30;
Console.WriteLine(age.HasValue); // True
Console.WriteLine(age.Value);    // 30

Always check HasValue (or != null) before accessing .Value.

GetValueOrDefault

Returns the value if present, or a fallback if null — without throwing.

C# Example Code
int? score = null;

int result1 = score.GetValueOrDefault();      // fallback = 0 (type default)
int result2 = score.GetValueOrDefault(-1);    // fallback = -1
int result3 = score ?? 0;                     // same as GetValueOrDefault(0)

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

score = 85;
Console.WriteLine(score.GetValueOrDefault(-1)); // 85

Null Checks

C# Example Code
int? quantity = GetQuantityFromDatabase(); // might be null

// All three are equivalent:
if (quantity.HasValue) { /* use quantity.Value */ }
if (quantity != null)  { /* use quantity.Value or quantity */ }
if (quantity is int q) { /* q is the unwrapped int */ }

// Pattern matching (C# 9+) — clearest for complex conditions
if (quantity is >= 1 and <= 100)
    Console.WriteLine("Valid quantity");

Null-Coalescing Operators

C# Example Code
int? timeout = null;

int actual    = timeout ?? 30;            // ?? — use 30 if null
timeout       ??= 30;                     // ??= — assign 30 if null, keep existing value
string label  = timeout?.ToString() ?? "none"; // ?. safe navigation + ??

Console.WriteLine(actual);   // 30
Console.WriteLine(timeout);  // 30
Console.WriteLine(label);    // 30

Arithmetic with Nullable Types

Any arithmetic involving a null nullable produces null — it propagates automatically.

C# Example Code
int? a = 10;
int? b = null;

Console.WriteLine(a + 5);   // 15
Console.WriteLine(a + b);   // (null)
Console.WriteLine(a * b);   // (null)
Console.WriteLine(b ?? 0);  // 0 — use ?? to collapse null before math

Comparisons

C# Example Code
int? x = 5;
int? y = null;

Console.WriteLine(x > 3);    // True
Console.WriteLine(y > 3);    // False — null comparisons always return false (except !=)
Console.WriteLine(y == null); // True
Console.WriteLine(y != 5);   // True

Nullable in Classes and Records

C# Example Code
public class Employee
{
    public string Name      { get; set; } = "";
    public DateTime? TerminationDate { get; set; } // null = still employed
    public decimal?  Bonus            { get; set; } // null = not yet determined
}

var emp = new Employee { Name = "Alice" };

string status = emp.TerminationDate.HasValue
    ? $"Left on {emp.TerminationDate.Value:d}"
    : "Currently employed";

Console.WriteLine(status); // Currently employed

decimal pay = 50_000 + (emp.Bonus ?? 0);
Console.WriteLine(pay);    // 50000

Nullable Value Type vs Nullable Reference Type

These are two separate features — don't confuse them:

Nullable value type (int?)Nullable reference type (string?)
IntroducedC# 2C# 8
PurposeAllow value types to represent nullAnnotate reference types as intentionally nullable
Runtime representationNullable<T> structSame string — annotation only
Checked atRuntime (HasValue)Compile-time (nullable warnings)
Default behaviorOpt-in (always available)Opt-in via <Nullable>enable</Nullable>
C# Example Code
// Nullable value type — runtime concept
int? count = null;

// Nullable reference type — compile-time warning annotation
string? name = null;  // "I know this can be null"
string  id   = null;  // ⚠️ warning: non-nullable assigned null

Common Patterns

Lift a value type to nullable for optional parameters

C# Example Code
// Optional date range filter
static IEnumerable<Order> GetOrders(DateTime? from = null, DateTime? to = null)
{
    var orders = AllOrders();
    if (from.HasValue) orders = orders.Where(o => o.Date >= from.Value);
    if (to.HasValue)   orders = orders.Where(o => o.Date <= to.Value);
    return orders;
}

Convert nullable to non-nullable safely

C# Example Code
int? raw = GetRawValue();

// Option 1 — ?? with a safe default
int value = raw ?? 0;

// Option 2 — guard and throw a meaningful error
int value2 = raw ?? throw new InvalidOperationException("Expected a value.");

// Option 3 — pattern matching
if (raw is int v)
{
    // use v
}

LINQ with nullable

C# Example Code
var records = new List<int?> { 1, null, 3, null, 5 };

// Filter nulls and unwrap
var values = records
    .Where(x => x.HasValue)
    .Select(x => x!.Value)
    .ToList();

Console.WriteLine(string.Join(", ", values)); // 1, 3, 5

// Sum ignoring nulls
int sum = records.Sum(x => x ?? 0);
Console.WriteLine(sum); // 9