YouTip LogoYouTip

Csharp Nullable

# C# Nullable Types ## C# Nullable Types In C#, types like `int`, `float`, `bool`, and `DateTime` are **value types**. By default, they must always have a value and cannot be `null`: ```csharp int a = null; // Compile error: Cannot convert null to 'int' **Nullable Types** solve this problem by allowing value types to have an additional "no value" state (i.e., `null`). This is very useful in scenarios like handling database fields or API return values where data might be missing. The declaration is simple: add a `?` after the type: ```csharp int? a = null; // Valid int? b = 42; // Valid Here, `int?` is syntactic sugar for `Nullable`. They are completely equivalent: ```csharp Nullable a = null; // Full form int? a = null; // Shorthand, same effect The diagram below illustrates how nullable types work in memoryβ€”they use an extra boolean flag to record "whether a value exists": * * * ## Difference Between Single Question Mark `?` and Double Question Mark `??` C# has two operators related to nullable types that are often used together but have completely different meanings: | Operator | Name | Purpose | Example | | :--- | :--- | :--- | :--- | | `?` | Nullable Type Modifier | Makes a value type nullable (can be `null`) | `int? i = 3;` is equivalent to `Nullable i = new Nullable(3);` | | `??` | Null-Coalescing Operator | Provides a default value when a variable is `null` | `int result = i ?? 0;` | A simple comparison: ```csharp int i; // Regular value type, default is 0, can never be null int? ii; // Nullable type, default is null * * * ## Declaration and Assignment of Nullable Types ### Declaration Syntax ```csharp ? = null; For example: ```csharp int? age = null; double? temperature = 36.6; bool? isActive = new bool?(); // Explicitly constructed, default is null DateTime? birthday = null; `Nullable` can represent the normal range of its underlying value type, plus an additional `null` value. For example, `Nullable` can be any integer from `-2,147,483,648` to `2,147,483,647`, or `null`. ### Complete Example ## Example ```csharp 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 values (null will display 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: num1 = 0 num2 = 45 num3 = 0 num4 = 3.14157 boolVal = * * * ## Null-Coalescing Operator (`??`) The `??` operator is used to provide a **fallback default value** for nullable types or reference types. It returns the left-hand value if it is not `null`; otherwise, it returns the right-hand value: ```csharp ?? * If `` is not `null`, it returns ``; * Otherwise, it returns ``. Starting from C# 8.0, you can also use `??=` (null-coalescing assignment operator), which assigns a value only if the variable is `null`: ```csharp int? x = null; x ??= 10; // x is null, assign 10 x ??= 20; // x already has value 10, no assignment ## Example ```csharp 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 value β†’ returns 3.14157 Console.WriteLine($"num1 ?? 5.34 = {result1}"); // 5.34 Console.WriteLine($"num2 ?? 5.34 = {result2}"); // 3.14157 // Chained usage: provide multiple fallback values int? a = null; int? b = null; int? c = 42; int value = a ?? b ?? c ?? 0; // Tries sequentially, ultimately gets 42 Console.WriteLine($"a ?? b ?? c ?? 0 = {value}"); // 42 } } } Output: 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` | Determines if the variable has a value (returns `bool`) | `if (num.HasValue) { ... }` | | `.Value` | Gets the actual value (throws `InvalidOperationException` if `null`) | `int x = num.Value;` | | `.GetValueOrDefault()` | Safely gets the value; returns the type's default value (e.g., `0`) if `null` | `num.GetValueOrDefault()` | | `.GetValueOrDefault(T)` | Safely gets the value; returns the specified default value if `null` | `num.GetValueOrDefault(100)` | | `??` | Null-coalescing operator (syntactic sugar, equivalent to `GetValueOrDefault`) | `int result = num ?? 100;` | ## Example ```csharp using System; namespace NullableDemo { class Program { static void Main(string[] args) { int? num = null; // HasValue + Value (Traditional approach) if (num.HasValue) Console.WriteLine($"Value is: {num.Value}"); else Console.WriteLine("num has no value"); // Safely get default value Console.WriteLine(num.GetValueOrDefault()); // 0 Console.WriteLine(num.GetValueOrDefault(99)); // 99 Console.WriteLine(num ?? 99); // 99 (Equivalent syntax) // ⚠️ Note: Accessing .Value directly 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 Database fields can be `NULL`. Nullable types can map to them precisely: | UserID | Age | IsActive | | :--- | :--- | :--- | | 1 | 28 | true | | 2 | null | false | ```csharp int? age = GetUserAgeFromDB(userId: 2); // Safely handles null string display = age.HasValue ? $"User Age: {age.Value}" : "Age unknown"; // Or a more concise way: string display2 = $"User Age: {age ?? 0}"; ### Optional Parameters and Configuration ```csharp // Configuration item might not be set int? timeout = GetConfigValue("timeout"); int actualTimeout = timeout ?? 30; // Use default 30 seconds if not set ### Chained Null Checking (C# 6.0+) ```csharp // Use ?. operator to safely access objects that might be null string? name = user?.Address?.City?.Name; // If user, Address, or City is null, the result is null, no exception thrown * * * ## Summary | Function | Example | Description | | :--- | :--- | :--- | | Define a nullable type | `int? x = null;` | Equivalent to `Nullable` | | Check if it has a value | `x.HasValue` | Returns `true` or `false` | | Get value (unsafe) | `x.Value` | Throws `InvalidOperationException` if `null` | | Get default value | `x ?? 0` | Returns the right-hand value if `null` | | Get specified default value | `x.GetValueOrDefault(10)` | Returns 10 if `null` | | Null-coalescing assignment | `x ??= 10` | Assigns only if `x` is `null` (C# 8.0+) | | Safe navigation access | `user?.Name` | Returns `null` if `user` is `null` (C# 6.0+) | * * * ## C# 8.0 "Nullable Reference Types" Starting with C# 8.0, **Nullable Reference Types** were introduced. This is a different mechanism from the **Nullable Value Types** discussed in this article: | Comparison Item | Nullable Value Types | Nullable Reference Types | | :--- | :--- | :--- | | Example | `int?` | `string?` | | Target | Value types (`int`, `bool`, `struct`, etc.) | Reference types (`string`, `class`, arrays, etc.) | | Implementation | Implemented at runtime via the `Nullable` struct | Static analysis by the compiler; does not change runtime behavior | | Default State | Always available (since C# 1.0) | Must be enabled in the project with `enable` | | null Check | Use `.HasValue` | Compiler issues warnings (not errors) | > Nullable Reference Types are a compile-time safety check toolβ€”they do not change the runtime behavior of your program, but they emit compiler warnings where code might produce a `NullReferenceException`, helping you catch potential issues during development.
← Csharp ArraySel Valid β†’