C#idiomatic C#
nullable-value-typescsharp/language-reference/builtin-types

Language

Nullable value types (C# reference)

Learn about C# nullable value types and how to use them

  • page csharp/language-reference/builtin-types/nullable-value-types
  • updated 01/14/2026

A nullable value type T? represents all values of its underlying value type T and an additional null value. For example, you can assign any of the following three values to a bool? variable: true, false, or null. An underlying value type T can't be a nullable value type itself.


ms.custom: "updateeachrelease"

The C# language reference documents the most recently released version of the C# language. It also contains initial documentation for features in public previews for the upcoming language release.

The documentation identifies any feature first introduced in the last three versions of the language or in current public previews.

Any nullable value type is an instance of the generic System.Nullable structure. You can refer to a nullable value type with an underlying type T in any of the following interchangeable forms: Nullable<T> or T?.

Typically, use a nullable value type when you need to represent the undefined value of an underlying value type. For example, a Boolean, or bool, variable can only be either true or false. However, in some applications a variable value can be undefined or missing. For example, a database field may contain true or false, or it might contain no value at all, that is, NULL. You can use the bool? type in that scenario.

Declaration and assignment

Because a value type is implicitly convertible to the corresponding nullable value type, you can assign a value to a variable of a nullable value type as you do that for its underlying value type. You can also assign the null value. For example:

sample · csharpdocs/csharp/language-reference/builtin-types/snippets/shared/NullableValueTypes.csDeclaration

csharp
namespace builtin_types;

public static class NullableValueTypes
{
    public static void Examples()
    {
        DeclareAndAssign();
        ExamineValueOfNullableType();
        UseNullCoalescingOperator();
        Operators();
        ComparisonOperators();
        BoxingAndUnboxing();
        WhetherTypeIsNullable();
        GetTypeExample();
        IsOperatorExample();
    }

    private static void DeclareAndAssign()
    {
        // <SnippetDeclaration>
        double? pi = 3.14;
        char? letter = 'a';

        int m2 = 10;
        int? m = m2;

        bool? flag = null;

        // An array of a nullable value type:
        int?[] arr = new int?[10];
        // </SnippetDeclaration>
    }

    private static void ExamineValueOfNullableType()
    {
        // <SnippetPatternMatching>
        int? a = 42;
        if (a is int valueOfA)
        {
            Console.WriteLine($"a is {valueOfA}");
        }
        else
        {
            Console.WriteLine("a does not have a value");
        }
        // Output:
        // a is 42
        // </SnippetPatternMatching>

        // <SnippetHasValue>
        int? b = 10;
        if (b.HasValue)
        {
            Console.WriteLine($"b is {b.Value}");
        }
        else
        {
            Console.WriteLine("b does not have a value");
        }
        // Output:
        // b is 10
        // </SnippetHasValue>

        // <SnippetCompareWithNull>
        int? c = 7;
        if (c != null)
        {
            Console.WriteLine($"c is {c.Value}");
        }
        else
        {
            Console.WriteLine("c does not have a value");
        }
        // Output:
        // c is 7
        // </SnippetCompareWithNull>
    }

    private static void UseNullCoalescingOperator()
    {
        // <SnippetNullCoalescing>
        int? a = 28;
        int b = a ?? -1;
        Console.WriteLine($"b is {b}");  // output: b is 28

        int? c = null;
        int d = c ?? -1;
        Console.WriteLine($"d is {d}");  // output: d is -1
        // </SnippetNullCoalescing>
    }

    private static void ExplicitCast()
    {
        // <SnippetCast>
        int? n = null;

        //int m1 = n;    // Doesn't compile
        int n2 = (int)n; // Compiles, but throws an exception if n is null
        // </SnippetCast>
    }

    private static void Operators()
    {
        // <SnippetLiftedOperator>
        int? a = 10;
        int? b = null;
        int? c = 10;

        a++;        // a is 11
        a = a * c;  // a is 110
        a = a + b;  // a is null
        // </SnippetLiftedOperator>
    }

    private static void ComparisonOperators()
    {
        // <SnippetComparisonOperators>
        int? a = 10;
        Console.WriteLine($"{a} >= null is {a >= null}");
        Console.WriteLine($"{a} < null is {a < null}");
        Console.WriteLine($"{a} == null is {a == null}");
        // Output:
        // 10 >= null is False
        // 10 < null is False
        // 10 == null is False

        int? b = null;
        int? c = null;
        Console.WriteLine($"null >= null is {b >= c}");
        Console.WriteLine($"null == null is {b == c}");
        // Output:
        // null >= null is False
        // null == null is True
        // </SnippetComparisonOperators>
    }

    private static void BoxingAndUnboxing()
    {
        // <SnippetBoxing>
        int a = 41;
        object aBoxed = a;
        int? aNullable = (int?)aBoxed;
        Console.WriteLine($"Value of aNullable: {aNullable}");

        object aNullableBoxed = aNullable;
        if (aNullableBoxed is int valueOfA)
        {
            Console.WriteLine($"aNullableBoxed is boxed int: {valueOfA}");
        }
        // Output:
        // Value of aNullable: 41
        // aNullableBoxed is boxed int: 41
        // </SnippetBoxing>
    }

    private static void WhetherTypeIsNullable()
    {
        // <SnippetIsTypeNullable>
        Console.WriteLine($"int? is {(IsNullable(typeof(int?)) ? "nullable" : "non nullable")} value type");
        Console.WriteLine($"int is {(IsNullable(typeof(int)) ? "nullable" : "non-nullable")} value type");

        bool IsNullable(Type type) => Nullable.GetUnderlyingType(type) != null;

        // Output:
        // int? is nullable value type
        // int is non-nullable value type
        // </SnippetIsTypeNullable>
    }

    private static void GetTypeExample()
    {
        // <SnippetGetType>
        int? a = 17;
        Type typeOfA = a.GetType();
        Console.WriteLine(typeOfA.FullName);
        // Output:
        // System.Int32
        // </SnippetGetType>
    }

    private static void IsOperatorExample()
    {
        // <SnippetIsOperator>
        int? a = 14;
        if (a is int)
        {
            Console.WriteLine("int? instance is compatible with int");
        }

        int b = 17;
        if (b is int?)
        {
            Console.WriteLine("int instance is compatible with int?");
        }
        // Output:
        // int? instance is compatible with int
        // int instance is compatible with int?
        // </SnippetIsOperator>
    }
}

The default value of a nullable value type represents null. It's an instance whose System.Nullable.HasValue property returns false.

Examination of an instance of a nullable value type

To check an instance of a nullable value type for null and get a value of an underlying type, use the is operator with a type pattern:

sample · csharpdocs/csharp/language-reference/builtin-types/snippets/shared/NullableValueTypes.csPatternMatching

csharp
namespace builtin_types;

public static class NullableValueTypes
{
    public static void Examples()
    {
        DeclareAndAssign();
        ExamineValueOfNullableType();
        UseNullCoalescingOperator();
        Operators();
        ComparisonOperators();
        BoxingAndUnboxing();
        WhetherTypeIsNullable();
        GetTypeExample();
        IsOperatorExample();
    }

    private static void DeclareAndAssign()
    {
        // <SnippetDeclaration>
        double? pi = 3.14;
        char? letter = 'a';

        int m2 = 10;
        int? m = m2;

        bool? flag = null;

        // An array of a nullable value type:
        int?[] arr = new int?[10];
        // </SnippetDeclaration>
    }

    private static void ExamineValueOfNullableType()
    {
        // <SnippetPatternMatching>
        int? a = 42;
        if (a is int valueOfA)
        {
            Console.WriteLine($"a is {valueOfA}");
        }
        else
        {
            Console.WriteLine("a does not have a value");
        }
        // Output:
        // a is 42
        // </SnippetPatternMatching>

        // <SnippetHasValue>
        int? b = 10;
        if (b.HasValue)
        {
            Console.WriteLine($"b is {b.Value}");
        }
        else
        {
            Console.WriteLine("b does not have a value");
        }
        // Output:
        // b is 10
        // </SnippetHasValue>

        // <SnippetCompareWithNull>
        int? c = 7;
        if (c != null)
        {
            Console.WriteLine($"c is {c.Value}");
        }
        else
        {
            Console.WriteLine("c does not have a value");
        }
        // Output:
        // c is 7
        // </SnippetCompareWithNull>
    }

    private static void UseNullCoalescingOperator()
    {
        // <SnippetNullCoalescing>
        int? a = 28;
        int b = a ?? -1;
        Console.WriteLine($"b is {b}");  // output: b is 28

        int? c = null;
        int d = c ?? -1;
        Console.WriteLine($"d is {d}");  // output: d is -1
        // </SnippetNullCoalescing>
    }

    private static void ExplicitCast()
    {
        // <SnippetCast>
        int? n = null;

        //int m1 = n;    // Doesn't compile
        int n2 = (int)n; // Compiles, but throws an exception if n is null
        // </SnippetCast>
    }

    private static void Operators()
    {
        // <SnippetLiftedOperator>
        int? a = 10;
        int? b = null;
        int? c = 10;

        a++;        // a is 11
        a = a * c;  // a is 110
        a = a + b;  // a is null
        // </SnippetLiftedOperator>
    }

    private static void ComparisonOperators()
    {
        // <SnippetComparisonOperators>
        int? a = 10;
        Console.WriteLine($"{a} >= null is {a >= null}");
        Console.WriteLine($"{a} < null is {a < null}");
        Console.WriteLine($"{a} == null is {a == null}");
        // Output:
        // 10 >= null is False
        // 10 < null is False
        // 10 == null is False

        int? b = null;
        int? c = null;
        Console.WriteLine($"null >= null is {b >= c}");
        Console.WriteLine($"null == null is {b == c}");
        // Output:
        // null >= null is False
        // null == null is True
        // </SnippetComparisonOperators>
    }

    private static void BoxingAndUnboxing()
    {
        // <SnippetBoxing>
        int a = 41;
        object aBoxed = a;
        int? aNullable = (int?)aBoxed;
        Console.WriteLine($"Value of aNullable: {aNullable}");

        object aNullableBoxed = aNullable;
        if (aNullableBoxed is int valueOfA)
        {
            Console.WriteLine($"aNullableBoxed is boxed int: {valueOfA}");
        }
        // Output:
        // Value of aNullable: 41
        // aNullableBoxed is boxed int: 41
        // </SnippetBoxing>
    }

    private static void WhetherTypeIsNullable()
    {
        // <SnippetIsTypeNullable>
        Console.WriteLine($"int? is {(IsNullable(typeof(int?)) ? "nullable" : "non nullable")} value type");
        Console.WriteLine($"int is {(IsNullable(typeof(int)) ? "nullable" : "non-nullable")} value type");

        bool IsNullable(Type type) => Nullable.GetUnderlyingType(type) != null;

        // Output:
        // int? is nullable value type
        // int is non-nullable value type
        // </SnippetIsTypeNullable>
    }

    private static void GetTypeExample()
    {
        // <SnippetGetType>
        int? a = 17;
        Type typeOfA = a.GetType();
        Console.WriteLine(typeOfA.FullName);
        // Output:
        // System.Int32
        // </SnippetGetType>
    }

    private static void IsOperatorExample()
    {
        // <SnippetIsOperator>
        int? a = 14;
        if (a is int)
        {
            Console.WriteLine("int? instance is compatible with int");
        }

        int b = 17;
        if (b is int?)
        {
            Console.WriteLine("int instance is compatible with int?");
        }
        // Output:
        // int? instance is compatible with int
        // int instance is compatible with int?
        // </SnippetIsOperator>
    }
}

You can always use the following read-only properties to check and get a value of a nullable value type variable:

The following example uses the HasValue property to check whether the variable contains a value before displaying it:

sample · csharpdocs/csharp/language-reference/builtin-types/snippets/shared/NullableValueTypes.csHasValue

csharp
namespace builtin_types;

public static class NullableValueTypes
{
    public static void Examples()
    {
        DeclareAndAssign();
        ExamineValueOfNullableType();
        UseNullCoalescingOperator();
        Operators();
        ComparisonOperators();
        BoxingAndUnboxing();
        WhetherTypeIsNullable();
        GetTypeExample();
        IsOperatorExample();
    }

    private static void DeclareAndAssign()
    {
        // <SnippetDeclaration>
        double? pi = 3.14;
        char? letter = 'a';

        int m2 = 10;
        int? m = m2;

        bool? flag = null;

        // An array of a nullable value type:
        int?[] arr = new int?[10];
        // </SnippetDeclaration>
    }

    private static void ExamineValueOfNullableType()
    {
        // <SnippetPatternMatching>
        int? a = 42;
        if (a is int valueOfA)
        {
            Console.WriteLine($"a is {valueOfA}");
        }
        else
        {
            Console.WriteLine("a does not have a value");
        }
        // Output:
        // a is 42
        // </SnippetPatternMatching>

        // <SnippetHasValue>
        int? b = 10;
        if (b.HasValue)
        {
            Console.WriteLine($"b is {b.Value}");
        }
        else
        {
            Console.WriteLine("b does not have a value");
        }
        // Output:
        // b is 10
        // </SnippetHasValue>

        // <SnippetCompareWithNull>
        int? c = 7;
        if (c != null)
        {
            Console.WriteLine($"c is {c.Value}");
        }
        else
        {
            Console.WriteLine("c does not have a value");
        }
        // Output:
        // c is 7
        // </SnippetCompareWithNull>
    }

    private static void UseNullCoalescingOperator()
    {
        // <SnippetNullCoalescing>
        int? a = 28;
        int b = a ?? -1;
        Console.WriteLine($"b is {b}");  // output: b is 28

        int? c = null;
        int d = c ?? -1;
        Console.WriteLine($"d is {d}");  // output: d is -1
        // </SnippetNullCoalescing>
    }

    private static void ExplicitCast()
    {
        // <SnippetCast>
        int? n = null;

        //int m1 = n;    // Doesn't compile
        int n2 = (int)n; // Compiles, but throws an exception if n is null
        // </SnippetCast>
    }

    private static void Operators()
    {
        // <SnippetLiftedOperator>
        int? a = 10;
        int? b = null;
        int? c = 10;

        a++;        // a is 11
        a = a * c;  // a is 110
        a = a + b;  // a is null
        // </SnippetLiftedOperator>
    }

    private static void ComparisonOperators()
    {
        // <SnippetComparisonOperators>
        int? a = 10;
        Console.WriteLine($"{a} >= null is {a >= null}");
        Console.WriteLine($"{a} < null is {a < null}");
        Console.WriteLine($"{a} == null is {a == null}");
        // Output:
        // 10 >= null is False
        // 10 < null is False
        // 10 == null is False

        int? b = null;
        int? c = null;
        Console.WriteLine($"null >= null is {b >= c}");
        Console.WriteLine($"null == null is {b == c}");
        // Output:
        // null >= null is False
        // null == null is True
        // </SnippetComparisonOperators>
    }

    private static void BoxingAndUnboxing()
    {
        // <SnippetBoxing>
        int a = 41;
        object aBoxed = a;
        int? aNullable = (int?)aBoxed;
        Console.WriteLine($"Value of aNullable: {aNullable}");

        object aNullableBoxed = aNullable;
        if (aNullableBoxed is int valueOfA)
        {
            Console.WriteLine($"aNullableBoxed is boxed int: {valueOfA}");
        }
        // Output:
        // Value of aNullable: 41
        // aNullableBoxed is boxed int: 41
        // </SnippetBoxing>
    }

    private static void WhetherTypeIsNullable()
    {
        // <SnippetIsTypeNullable>
        Console.WriteLine($"int? is {(IsNullable(typeof(int?)) ? "nullable" : "non nullable")} value type");
        Console.WriteLine($"int is {(IsNullable(typeof(int)) ? "nullable" : "non-nullable")} value type");

        bool IsNullable(Type type) => Nullable.GetUnderlyingType(type) != null;

        // Output:
        // int? is nullable value type
        // int is non-nullable value type
        // </SnippetIsTypeNullable>
    }

    private static void GetTypeExample()
    {
        // <SnippetGetType>
        int? a = 17;
        Type typeOfA = a.GetType();
        Console.WriteLine(typeOfA.FullName);
        // Output:
        // System.Int32
        // </SnippetGetType>
    }

    private static void IsOperatorExample()
    {
        // <SnippetIsOperator>
        int? a = 14;
        if (a is int)
        {
            Console.WriteLine("int? instance is compatible with int");
        }

        int b = 17;
        if (b is int?)
        {
            Console.WriteLine("int instance is compatible with int?");
        }
        // Output:
        // int? instance is compatible with int
        // int instance is compatible with int?
        // </SnippetIsOperator>
    }
}

You can also compare a variable of a nullable value type with null instead of using the HasValue property, as the following example shows:

sample · csharpdocs/csharp/language-reference/builtin-types/snippets/shared/NullableValueTypes.csCompareWithNull

csharp
namespace builtin_types;

public static class NullableValueTypes
{
    public static void Examples()
    {
        DeclareAndAssign();
        ExamineValueOfNullableType();
        UseNullCoalescingOperator();
        Operators();
        ComparisonOperators();
        BoxingAndUnboxing();
        WhetherTypeIsNullable();
        GetTypeExample();
        IsOperatorExample();
    }

    private static void DeclareAndAssign()
    {
        // <SnippetDeclaration>
        double? pi = 3.14;
        char? letter = 'a';

        int m2 = 10;
        int? m = m2;

        bool? flag = null;

        // An array of a nullable value type:
        int?[] arr = new int?[10];
        // </SnippetDeclaration>
    }

    private static void ExamineValueOfNullableType()
    {
        // <SnippetPatternMatching>
        int? a = 42;
        if (a is int valueOfA)
        {
            Console.WriteLine($"a is {valueOfA}");
        }
        else
        {
            Console.WriteLine("a does not have a value");
        }
        // Output:
        // a is 42
        // </SnippetPatternMatching>

        // <SnippetHasValue>
        int? b = 10;
        if (b.HasValue)
        {
            Console.WriteLine($"b is {b.Value}");
        }
        else
        {
            Console.WriteLine("b does not have a value");
        }
        // Output:
        // b is 10
        // </SnippetHasValue>

        // <SnippetCompareWithNull>
        int? c = 7;
        if (c != null)
        {
            Console.WriteLine($"c is {c.Value}");
        }
        else
        {
            Console.WriteLine("c does not have a value");
        }
        // Output:
        // c is 7
        // </SnippetCompareWithNull>
    }

    private static void UseNullCoalescingOperator()
    {
        // <SnippetNullCoalescing>
        int? a = 28;
        int b = a ?? -1;
        Console.WriteLine($"b is {b}");  // output: b is 28

        int? c = null;
        int d = c ?? -1;
        Console.WriteLine($"d is {d}");  // output: d is -1
        // </SnippetNullCoalescing>
    }

    private static void ExplicitCast()
    {
        // <SnippetCast>
        int? n = null;

        //int m1 = n;    // Doesn't compile
        int n2 = (int)n; // Compiles, but throws an exception if n is null
        // </SnippetCast>
    }

    private static void Operators()
    {
        // <SnippetLiftedOperator>
        int? a = 10;
        int? b = null;
        int? c = 10;

        a++;        // a is 11
        a = a * c;  // a is 110
        a = a + b;  // a is null
        // </SnippetLiftedOperator>
    }

    private static void ComparisonOperators()
    {
        // <SnippetComparisonOperators>
        int? a = 10;
        Console.WriteLine($"{a} >= null is {a >= null}");
        Console.WriteLine($"{a} < null is {a < null}");
        Console.WriteLine($"{a} == null is {a == null}");
        // Output:
        // 10 >= null is False
        // 10 < null is False
        // 10 == null is False

        int? b = null;
        int? c = null;
        Console.WriteLine($"null >= null is {b >= c}");
        Console.WriteLine($"null == null is {b == c}");
        // Output:
        // null >= null is False
        // null == null is True
        // </SnippetComparisonOperators>
    }

    private static void BoxingAndUnboxing()
    {
        // <SnippetBoxing>
        int a = 41;
        object aBoxed = a;
        int? aNullable = (int?)aBoxed;
        Console.WriteLine($"Value of aNullable: {aNullable}");

        object aNullableBoxed = aNullable;
        if (aNullableBoxed is int valueOfA)
        {
            Console.WriteLine($"aNullableBoxed is boxed int: {valueOfA}");
        }
        // Output:
        // Value of aNullable: 41
        // aNullableBoxed is boxed int: 41
        // </SnippetBoxing>
    }

    private static void WhetherTypeIsNullable()
    {
        // <SnippetIsTypeNullable>
        Console.WriteLine($"int? is {(IsNullable(typeof(int?)) ? "nullable" : "non nullable")} value type");
        Console.WriteLine($"int is {(IsNullable(typeof(int)) ? "nullable" : "non-nullable")} value type");

        bool IsNullable(Type type) => Nullable.GetUnderlyingType(type) != null;

        // Output:
        // int? is nullable value type
        // int is non-nullable value type
        // </SnippetIsTypeNullable>
    }

    private static void GetTypeExample()
    {
        // <SnippetGetType>
        int? a = 17;
        Type typeOfA = a.GetType();
        Console.WriteLine(typeOfA.FullName);
        // Output:
        // System.Int32
        // </SnippetGetType>
    }

    private static void IsOperatorExample()
    {
        // <SnippetIsOperator>
        int? a = 14;
        if (a is int)
        {
            Console.WriteLine("int? instance is compatible with int");
        }

        int b = 17;
        if (b is int?)
        {
            Console.WriteLine("int instance is compatible with int?");
        }
        // Output:
        // int? instance is compatible with int
        // int instance is compatible with int?
        // </SnippetIsOperator>
    }
}

Conversion from a nullable value type to an underlying type

If you want to assign a value of a nullable value type to a non-nullable value type variable, you might need to specify the value to assign in place of null. Use the null-coalescing operator ?? to do that. You can also use the System.Nullable.GetValueOrDefault() method for the same purpose:

sample · csharpdocs/csharp/language-reference/builtin-types/snippets/shared/NullableValueTypes.csNullCoalescing

csharp
namespace builtin_types;

public static class NullableValueTypes
{
    public static void Examples()
    {
        DeclareAndAssign();
        ExamineValueOfNullableType();
        UseNullCoalescingOperator();
        Operators();
        ComparisonOperators();
        BoxingAndUnboxing();
        WhetherTypeIsNullable();
        GetTypeExample();
        IsOperatorExample();
    }

    private static void DeclareAndAssign()
    {
        // <SnippetDeclaration>
        double? pi = 3.14;
        char? letter = 'a';

        int m2 = 10;
        int? m = m2;

        bool? flag = null;

        // An array of a nullable value type:
        int?[] arr = new int?[10];
        // </SnippetDeclaration>
    }

    private static void ExamineValueOfNullableType()
    {
        // <SnippetPatternMatching>
        int? a = 42;
        if (a is int valueOfA)
        {
            Console.WriteLine($"a is {valueOfA}");
        }
        else
        {
            Console.WriteLine("a does not have a value");
        }
        // Output:
        // a is 42
        // </SnippetPatternMatching>

        // <SnippetHasValue>
        int? b = 10;
        if (b.HasValue)
        {
            Console.WriteLine($"b is {b.Value}");
        }
        else
        {
            Console.WriteLine("b does not have a value");
        }
        // Output:
        // b is 10
        // </SnippetHasValue>

        // <SnippetCompareWithNull>
        int? c = 7;
        if (c != null)
        {
            Console.WriteLine($"c is {c.Value}");
        }
        else
        {
            Console.WriteLine("c does not have a value");
        }
        // Output:
        // c is 7
        // </SnippetCompareWithNull>
    }

    private static void UseNullCoalescingOperator()
    {
        // <SnippetNullCoalescing>
        int? a = 28;
        int b = a ?? -1;
        Console.WriteLine($"b is {b}");  // output: b is 28

        int? c = null;
        int d = c ?? -1;
        Console.WriteLine($"d is {d}");  // output: d is -1
        // </SnippetNullCoalescing>
    }

    private static void ExplicitCast()
    {
        // <SnippetCast>
        int? n = null;

        //int m1 = n;    // Doesn't compile
        int n2 = (int)n; // Compiles, but throws an exception if n is null
        // </SnippetCast>
    }

    private static void Operators()
    {
        // <SnippetLiftedOperator>
        int? a = 10;
        int? b = null;
        int? c = 10;

        a++;        // a is 11
        a = a * c;  // a is 110
        a = a + b;  // a is null
        // </SnippetLiftedOperator>
    }

    private static void ComparisonOperators()
    {
        // <SnippetComparisonOperators>
        int? a = 10;
        Console.WriteLine($"{a} >= null is {a >= null}");
        Console.WriteLine($"{a} < null is {a < null}");
        Console.WriteLine($"{a} == null is {a == null}");
        // Output:
        // 10 >= null is False
        // 10 < null is False
        // 10 == null is False

        int? b = null;
        int? c = null;
        Console.WriteLine($"null >= null is {b >= c}");
        Console.WriteLine($"null == null is {b == c}");
        // Output:
        // null >= null is False
        // null == null is True
        // </SnippetComparisonOperators>
    }

    private static void BoxingAndUnboxing()
    {
        // <SnippetBoxing>
        int a = 41;
        object aBoxed = a;
        int? aNullable = (int?)aBoxed;
        Console.WriteLine($"Value of aNullable: {aNullable}");

        object aNullableBoxed = aNullable;
        if (aNullableBoxed is int valueOfA)
        {
            Console.WriteLine($"aNullableBoxed is boxed int: {valueOfA}");
        }
        // Output:
        // Value of aNullable: 41
        // aNullableBoxed is boxed int: 41
        // </SnippetBoxing>
    }

    private static void WhetherTypeIsNullable()
    {
        // <SnippetIsTypeNullable>
        Console.WriteLine($"int? is {(IsNullable(typeof(int?)) ? "nullable" : "non nullable")} value type");
        Console.WriteLine($"int is {(IsNullable(typeof(int)) ? "nullable" : "non-nullable")} value type");

        bool IsNullable(Type type) => Nullable.GetUnderlyingType(type) != null;

        // Output:
        // int? is nullable value type
        // int is non-nullable value type
        // </SnippetIsTypeNullable>
    }

    private static void GetTypeExample()
    {
        // <SnippetGetType>
        int? a = 17;
        Type typeOfA = a.GetType();
        Console.WriteLine(typeOfA.FullName);
        // Output:
        // System.Int32
        // </SnippetGetType>
    }

    private static void IsOperatorExample()
    {
        // <SnippetIsOperator>
        int? a = 14;
        if (a is int)
        {
            Console.WriteLine("int? instance is compatible with int");
        }

        int b = 17;
        if (b is int?)
        {
            Console.WriteLine("int instance is compatible with int?");
        }
        // Output:
        // int? instance is compatible with int
        // int instance is compatible with int?
        // </SnippetIsOperator>
    }
}

If you want to use the default value of the underlying value type in place of null, use the System.Nullable.GetValueOrDefault method.

You can also explicitly cast a nullable value type to a non-nullable type, as the following example shows:

sample · csharpdocs/csharp/language-reference/builtin-types/snippets/shared/NullableValueTypes.csCast

csharp
namespace builtin_types;

public static class NullableValueTypes
{
    public static void Examples()
    {
        DeclareAndAssign();
        ExamineValueOfNullableType();
        UseNullCoalescingOperator();
        Operators();
        ComparisonOperators();
        BoxingAndUnboxing();
        WhetherTypeIsNullable();
        GetTypeExample();
        IsOperatorExample();
    }

    private static void DeclareAndAssign()
    {
        // <SnippetDeclaration>
        double? pi = 3.14;
        char? letter = 'a';

        int m2 = 10;
        int? m = m2;

        bool? flag = null;

        // An array of a nullable value type:
        int?[] arr = new int?[10];
        // </SnippetDeclaration>
    }

    private static void ExamineValueOfNullableType()
    {
        // <SnippetPatternMatching>
        int? a = 42;
        if (a is int valueOfA)
        {
            Console.WriteLine($"a is {valueOfA}");
        }
        else
        {
            Console.WriteLine("a does not have a value");
        }
        // Output:
        // a is 42
        // </SnippetPatternMatching>

        // <SnippetHasValue>
        int? b = 10;
        if (b.HasValue)
        {
            Console.WriteLine($"b is {b.Value}");
        }
        else
        {
            Console.WriteLine("b does not have a value");
        }
        // Output:
        // b is 10
        // </SnippetHasValue>

        // <SnippetCompareWithNull>
        int? c = 7;
        if (c != null)
        {
            Console.WriteLine($"c is {c.Value}");
        }
        else
        {
            Console.WriteLine("c does not have a value");
        }
        // Output:
        // c is 7
        // </SnippetCompareWithNull>
    }

    private static void UseNullCoalescingOperator()
    {
        // <SnippetNullCoalescing>
        int? a = 28;
        int b = a ?? -1;
        Console.WriteLine($"b is {b}");  // output: b is 28

        int? c = null;
        int d = c ?? -1;
        Console.WriteLine($"d is {d}");  // output: d is -1
        // </SnippetNullCoalescing>
    }

    private static void ExplicitCast()
    {
        // <SnippetCast>
        int? n = null;

        //int m1 = n;    // Doesn't compile
        int n2 = (int)n; // Compiles, but throws an exception if n is null
        // </SnippetCast>
    }

    private static void Operators()
    {
        // <SnippetLiftedOperator>
        int? a = 10;
        int? b = null;
        int? c = 10;

        a++;        // a is 11
        a = a * c;  // a is 110
        a = a + b;  // a is null
        // </SnippetLiftedOperator>
    }

    private static void ComparisonOperators()
    {
        // <SnippetComparisonOperators>
        int? a = 10;
        Console.WriteLine($"{a} >= null is {a >= null}");
        Console.WriteLine($"{a} < null is {a < null}");
        Console.WriteLine($"{a} == null is {a == null}");
        // Output:
        // 10 >= null is False
        // 10 < null is False
        // 10 == null is False

        int? b = null;
        int? c = null;
        Console.WriteLine($"null >= null is {b >= c}");
        Console.WriteLine($"null == null is {b == c}");
        // Output:
        // null >= null is False
        // null == null is True
        // </SnippetComparisonOperators>
    }

    private static void BoxingAndUnboxing()
    {
        // <SnippetBoxing>
        int a = 41;
        object aBoxed = a;
        int? aNullable = (int?)aBoxed;
        Console.WriteLine($"Value of aNullable: {aNullable}");

        object aNullableBoxed = aNullable;
        if (aNullableBoxed is int valueOfA)
        {
            Console.WriteLine($"aNullableBoxed is boxed int: {valueOfA}");
        }
        // Output:
        // Value of aNullable: 41
        // aNullableBoxed is boxed int: 41
        // </SnippetBoxing>
    }

    private static void WhetherTypeIsNullable()
    {
        // <SnippetIsTypeNullable>
        Console.WriteLine($"int? is {(IsNullable(typeof(int?)) ? "nullable" : "non nullable")} value type");
        Console.WriteLine($"int is {(IsNullable(typeof(int)) ? "nullable" : "non-nullable")} value type");

        bool IsNullable(Type type) => Nullable.GetUnderlyingType(type) != null;

        // Output:
        // int? is nullable value type
        // int is non-nullable value type
        // </SnippetIsTypeNullable>
    }

    private static void GetTypeExample()
    {
        // <SnippetGetType>
        int? a = 17;
        Type typeOfA = a.GetType();
        Console.WriteLine(typeOfA.FullName);
        // Output:
        // System.Int32
        // </SnippetGetType>
    }

    private static void IsOperatorExample()
    {
        // <SnippetIsOperator>
        int? a = 14;
        if (a is int)
        {
            Console.WriteLine("int? instance is compatible with int");
        }

        int b = 17;
        if (b is int?)
        {
            Console.WriteLine("int instance is compatible with int?");
        }
        // Output:
        // int? instance is compatible with int
        // int instance is compatible with int?
        // </SnippetIsOperator>
    }
}

At run time, if the value of a nullable value type is null, the explicit cast throws an System.InvalidOperationException.

A non-nullable value type T is implicitly convertible to the corresponding nullable value type T?.

Lifted operators

A nullable value type T? supports the predefined unary and binary operators or any overloaded operators that a value type T supports. These operators, also known as lifted operators, return null if one or both operands are null. Otherwise, the operator uses the contained values of its operands to calculate the result. For example:

sample · csharpdocs/csharp/language-reference/builtin-types/snippets/shared/NullableValueTypes.csLiftedOperator

csharp
namespace builtin_types;

public static class NullableValueTypes
{
    public static void Examples()
    {
        DeclareAndAssign();
        ExamineValueOfNullableType();
        UseNullCoalescingOperator();
        Operators();
        ComparisonOperators();
        BoxingAndUnboxing();
        WhetherTypeIsNullable();
        GetTypeExample();
        IsOperatorExample();
    }

    private static void DeclareAndAssign()
    {
        // <SnippetDeclaration>
        double? pi = 3.14;
        char? letter = 'a';

        int m2 = 10;
        int? m = m2;

        bool? flag = null;

        // An array of a nullable value type:
        int?[] arr = new int?[10];
        // </SnippetDeclaration>
    }

    private static void ExamineValueOfNullableType()
    {
        // <SnippetPatternMatching>
        int? a = 42;
        if (a is int valueOfA)
        {
            Console.WriteLine($"a is {valueOfA}");
        }
        else
        {
            Console.WriteLine("a does not have a value");
        }
        // Output:
        // a is 42
        // </SnippetPatternMatching>

        // <SnippetHasValue>
        int? b = 10;
        if (b.HasValue)
        {
            Console.WriteLine($"b is {b.Value}");
        }
        else
        {
            Console.WriteLine("b does not have a value");
        }
        // Output:
        // b is 10
        // </SnippetHasValue>

        // <SnippetCompareWithNull>
        int? c = 7;
        if (c != null)
        {
            Console.WriteLine($"c is {c.Value}");
        }
        else
        {
            Console.WriteLine("c does not have a value");
        }
        // Output:
        // c is 7
        // </SnippetCompareWithNull>
    }

    private static void UseNullCoalescingOperator()
    {
        // <SnippetNullCoalescing>
        int? a = 28;
        int b = a ?? -1;
        Console.WriteLine($"b is {b}");  // output: b is 28

        int? c = null;
        int d = c ?? -1;
        Console.WriteLine($"d is {d}");  // output: d is -1
        // </SnippetNullCoalescing>
    }

    private static void ExplicitCast()
    {
        // <SnippetCast>
        int? n = null;

        //int m1 = n;    // Doesn't compile
        int n2 = (int)n; // Compiles, but throws an exception if n is null
        // </SnippetCast>
    }

    private static void Operators()
    {
        // <SnippetLiftedOperator>
        int? a = 10;
        int? b = null;
        int? c = 10;

        a++;        // a is 11
        a = a * c;  // a is 110
        a = a + b;  // a is null
        // </SnippetLiftedOperator>
    }

    private static void ComparisonOperators()
    {
        // <SnippetComparisonOperators>
        int? a = 10;
        Console.WriteLine($"{a} >= null is {a >= null}");
        Console.WriteLine($"{a} < null is {a < null}");
        Console.WriteLine($"{a} == null is {a == null}");
        // Output:
        // 10 >= null is False
        // 10 < null is False
        // 10 == null is False

        int? b = null;
        int? c = null;
        Console.WriteLine($"null >= null is {b >= c}");
        Console.WriteLine($"null == null is {b == c}");
        // Output:
        // null >= null is False
        // null == null is True
        // </SnippetComparisonOperators>
    }

    private static void BoxingAndUnboxing()
    {
        // <SnippetBoxing>
        int a = 41;
        object aBoxed = a;
        int? aNullable = (int?)aBoxed;
        Console.WriteLine($"Value of aNullable: {aNullable}");

        object aNullableBoxed = aNullable;
        if (aNullableBoxed is int valueOfA)
        {
            Console.WriteLine($"aNullableBoxed is boxed int: {valueOfA}");
        }
        // Output:
        // Value of aNullable: 41
        // aNullableBoxed is boxed int: 41
        // </SnippetBoxing>
    }

    private static void WhetherTypeIsNullable()
    {
        // <SnippetIsTypeNullable>
        Console.WriteLine($"int? is {(IsNullable(typeof(int?)) ? "nullable" : "non nullable")} value type");
        Console.WriteLine($"int is {(IsNullable(typeof(int)) ? "nullable" : "non-nullable")} value type");

        bool IsNullable(Type type) => Nullable.GetUnderlyingType(type) != null;

        // Output:
        // int? is nullable value type
        // int is non-nullable value type
        // </SnippetIsTypeNullable>
    }

    private static void GetTypeExample()
    {
        // <SnippetGetType>
        int? a = 17;
        Type typeOfA = a.GetType();
        Console.WriteLine(typeOfA.FullName);
        // Output:
        // System.Int32
        // </SnippetGetType>
    }

    private static void IsOperatorExample()
    {
        // <SnippetIsOperator>
        int? a = 14;
        if (a is int)
        {
            Console.WriteLine("int? instance is compatible with int");
        }

        int b = 17;
        if (b is int?)
        {
            Console.WriteLine("int instance is compatible with int?");
        }
        // Output:
        // int? instance is compatible with int
        // int instance is compatible with int?
        // </SnippetIsOperator>
    }
}

For the comparison operators <, >, <=, and >=, if one or both operands are null, the result is false. Otherwise, the contained values of operands are compared. Don't assume that because a particular comparison (for example, <=) returns false, the opposite comparison (>) returns true. The following example shows that 10 is

  • neither greater than or equal to null
  • nor less than null

sample · csharpdocs/csharp/language-reference/builtin-types/snippets/shared/NullableValueTypes.csComparisonOperators

csharp
namespace builtin_types;

public static class NullableValueTypes
{
    public static void Examples()
    {
        DeclareAndAssign();
        ExamineValueOfNullableType();
        UseNullCoalescingOperator();
        Operators();
        ComparisonOperators();
        BoxingAndUnboxing();
        WhetherTypeIsNullable();
        GetTypeExample();
        IsOperatorExample();
    }

    private static void DeclareAndAssign()
    {
        // <SnippetDeclaration>
        double? pi = 3.14;
        char? letter = 'a';

        int m2 = 10;
        int? m = m2;

        bool? flag = null;

        // An array of a nullable value type:
        int?[] arr = new int?[10];
        // </SnippetDeclaration>
    }

    private static void ExamineValueOfNullableType()
    {
        // <SnippetPatternMatching>
        int? a = 42;
        if (a is int valueOfA)
        {
            Console.WriteLine($"a is {valueOfA}");
        }
        else
        {
            Console.WriteLine("a does not have a value");
        }
        // Output:
        // a is 42
        // </SnippetPatternMatching>

        // <SnippetHasValue>
        int? b = 10;
        if (b.HasValue)
        {
            Console.WriteLine($"b is {b.Value}");
        }
        else
        {
            Console.WriteLine("b does not have a value");
        }
        // Output:
        // b is 10
        // </SnippetHasValue>

        // <SnippetCompareWithNull>
        int? c = 7;
        if (c != null)
        {
            Console.WriteLine($"c is {c.Value}");
        }
        else
        {
            Console.WriteLine("c does not have a value");
        }
        // Output:
        // c is 7
        // </SnippetCompareWithNull>
    }

    private static void UseNullCoalescingOperator()
    {
        // <SnippetNullCoalescing>
        int? a = 28;
        int b = a ?? -1;
        Console.WriteLine($"b is {b}");  // output: b is 28

        int? c = null;
        int d = c ?? -1;
        Console.WriteLine($"d is {d}");  // output: d is -1
        // </SnippetNullCoalescing>
    }

    private static void ExplicitCast()
    {
        // <SnippetCast>
        int? n = null;

        //int m1 = n;    // Doesn't compile
        int n2 = (int)n; // Compiles, but throws an exception if n is null
        // </SnippetCast>
    }

    private static void Operators()
    {
        // <SnippetLiftedOperator>
        int? a = 10;
        int? b = null;
        int? c = 10;

        a++;        // a is 11
        a = a * c;  // a is 110
        a = a + b;  // a is null
        // </SnippetLiftedOperator>
    }

    private static void ComparisonOperators()
    {
        // <SnippetComparisonOperators>
        int? a = 10;
        Console.WriteLine($"{a} >= null is {a >= null}");
        Console.WriteLine($"{a} < null is {a < null}");
        Console.WriteLine($"{a} == null is {a == null}");
        // Output:
        // 10 >= null is False
        // 10 < null is False
        // 10 == null is False

        int? b = null;
        int? c = null;
        Console.WriteLine($"null >= null is {b >= c}");
        Console.WriteLine($"null == null is {b == c}");
        // Output:
        // null >= null is False
        // null == null is True
        // </SnippetComparisonOperators>
    }

    private static void BoxingAndUnboxing()
    {
        // <SnippetBoxing>
        int a = 41;
        object aBoxed = a;
        int? aNullable = (int?)aBoxed;
        Console.WriteLine($"Value of aNullable: {aNullable}");

        object aNullableBoxed = aNullable;
        if (aNullableBoxed is int valueOfA)
        {
            Console.WriteLine($"aNullableBoxed is boxed int: {valueOfA}");
        }
        // Output:
        // Value of aNullable: 41
        // aNullableBoxed is boxed int: 41
        // </SnippetBoxing>
    }

    private static void WhetherTypeIsNullable()
    {
        // <SnippetIsTypeNullable>
        Console.WriteLine($"int? is {(IsNullable(typeof(int?)) ? "nullable" : "non nullable")} value type");
        Console.WriteLine($"int is {(IsNullable(typeof(int)) ? "nullable" : "non-nullable")} value type");

        bool IsNullable(Type type) => Nullable.GetUnderlyingType(type) != null;

        // Output:
        // int? is nullable value type
        // int is non-nullable value type
        // </SnippetIsTypeNullable>
    }

    private static void GetTypeExample()
    {
        // <SnippetGetType>
        int? a = 17;
        Type typeOfA = a.GetType();
        Console.WriteLine(typeOfA.FullName);
        // Output:
        // System.Int32
        // </SnippetGetType>
    }

    private static void IsOperatorExample()
    {
        // <SnippetIsOperator>
        int? a = 14;
        if (a is int)
        {
            Console.WriteLine("int? instance is compatible with int");
        }

        int b = 17;
        if (b is int?)
        {
            Console.WriteLine("int instance is compatible with int?");
        }
        // Output:
        // int? instance is compatible with int
        // int instance is compatible with int?
        // </SnippetIsOperator>
    }
}

For the equality operator ==, if both operands are null, the result is true. If only one of the operands is null, the result is false. Otherwise, the contained values of operands are compared.

For the inequality operator !=, if both operands are null, the result is false. If only one of the operands is null, the result is true. Otherwise, the contained values of operands are compared.

If a user-defined conversion exists between two value types, the same conversion can also be used between the corresponding nullable value types.

Boxing and unboxing

The following rules apply when you box an instance of a nullable value type T?:

You can unbox a boxed value of a value type T to the corresponding nullable value type T?, as the following example shows:

sample · csharpdocs/csharp/language-reference/builtin-types/snippets/shared/NullableValueTypes.csBoxing

csharp
namespace builtin_types;

public static class NullableValueTypes
{
    public static void Examples()
    {
        DeclareAndAssign();
        ExamineValueOfNullableType();
        UseNullCoalescingOperator();
        Operators();
        ComparisonOperators();
        BoxingAndUnboxing();
        WhetherTypeIsNullable();
        GetTypeExample();
        IsOperatorExample();
    }

    private static void DeclareAndAssign()
    {
        // <SnippetDeclaration>
        double? pi = 3.14;
        char? letter = 'a';

        int m2 = 10;
        int? m = m2;

        bool? flag = null;

        // An array of a nullable value type:
        int?[] arr = new int?[10];
        // </SnippetDeclaration>
    }

    private static void ExamineValueOfNullableType()
    {
        // <SnippetPatternMatching>
        int? a = 42;
        if (a is int valueOfA)
        {
            Console.WriteLine($"a is {valueOfA}");
        }
        else
        {
            Console.WriteLine("a does not have a value");
        }
        // Output:
        // a is 42
        // </SnippetPatternMatching>

        // <SnippetHasValue>
        int? b = 10;
        if (b.HasValue)
        {
            Console.WriteLine($"b is {b.Value}");
        }
        else
        {
            Console.WriteLine("b does not have a value");
        }
        // Output:
        // b is 10
        // </SnippetHasValue>

        // <SnippetCompareWithNull>
        int? c = 7;
        if (c != null)
        {
            Console.WriteLine($"c is {c.Value}");
        }
        else
        {
            Console.WriteLine("c does not have a value");
        }
        // Output:
        // c is 7
        // </SnippetCompareWithNull>
    }

    private static void UseNullCoalescingOperator()
    {
        // <SnippetNullCoalescing>
        int? a = 28;
        int b = a ?? -1;
        Console.WriteLine($"b is {b}");  // output: b is 28

        int? c = null;
        int d = c ?? -1;
        Console.WriteLine($"d is {d}");  // output: d is -1
        // </SnippetNullCoalescing>
    }

    private static void ExplicitCast()
    {
        // <SnippetCast>
        int? n = null;

        //int m1 = n;    // Doesn't compile
        int n2 = (int)n; // Compiles, but throws an exception if n is null
        // </SnippetCast>
    }

    private static void Operators()
    {
        // <SnippetLiftedOperator>
        int? a = 10;
        int? b = null;
        int? c = 10;

        a++;        // a is 11
        a = a * c;  // a is 110
        a = a + b;  // a is null
        // </SnippetLiftedOperator>
    }

    private static void ComparisonOperators()
    {
        // <SnippetComparisonOperators>
        int? a = 10;
        Console.WriteLine($"{a} >= null is {a >= null}");
        Console.WriteLine($"{a} < null is {a < null}");
        Console.WriteLine($"{a} == null is {a == null}");
        // Output:
        // 10 >= null is False
        // 10 < null is False
        // 10 == null is False

        int? b = null;
        int? c = null;
        Console.WriteLine($"null >= null is {b >= c}");
        Console.WriteLine($"null == null is {b == c}");
        // Output:
        // null >= null is False
        // null == null is True
        // </SnippetComparisonOperators>
    }

    private static void BoxingAndUnboxing()
    {
        // <SnippetBoxing>
        int a = 41;
        object aBoxed = a;
        int? aNullable = (int?)aBoxed;
        Console.WriteLine($"Value of aNullable: {aNullable}");

        object aNullableBoxed = aNullable;
        if (aNullableBoxed is int valueOfA)
        {
            Console.WriteLine($"aNullableBoxed is boxed int: {valueOfA}");
        }
        // Output:
        // Value of aNullable: 41
        // aNullableBoxed is boxed int: 41
        // </SnippetBoxing>
    }

    private static void WhetherTypeIsNullable()
    {
        // <SnippetIsTypeNullable>
        Console.WriteLine($"int? is {(IsNullable(typeof(int?)) ? "nullable" : "non nullable")} value type");
        Console.WriteLine($"int is {(IsNullable(typeof(int)) ? "nullable" : "non-nullable")} value type");

        bool IsNullable(Type type) => Nullable.GetUnderlyingType(type) != null;

        // Output:
        // int? is nullable value type
        // int is non-nullable value type
        // </SnippetIsTypeNullable>
    }

    private static void GetTypeExample()
    {
        // <SnippetGetType>
        int? a = 17;
        Type typeOfA = a.GetType();
        Console.WriteLine(typeOfA.FullName);
        // Output:
        // System.Int32
        // </SnippetGetType>
    }

    private static void IsOperatorExample()
    {
        // <SnippetIsOperator>
        int? a = 14;
        if (a is int)
        {
            Console.WriteLine("int? instance is compatible with int");
        }

        int b = 17;
        if (b is int?)
        {
            Console.WriteLine("int instance is compatible with int?");
        }
        // Output:
        // int? instance is compatible with int
        // int instance is compatible with int?
        // </SnippetIsOperator>
    }
}

How to identify a nullable value type

The following example shows how to determine whether a System.Type instance represents a constructed nullable value type, that is, the System.Nullable type with a specified type parameter T:

sample · csharpdocs/csharp/language-reference/builtin-types/snippets/shared/NullableValueTypes.csIsTypeNullable

csharp
namespace builtin_types;

public static class NullableValueTypes
{
    public static void Examples()
    {
        DeclareAndAssign();
        ExamineValueOfNullableType();
        UseNullCoalescingOperator();
        Operators();
        ComparisonOperators();
        BoxingAndUnboxing();
        WhetherTypeIsNullable();
        GetTypeExample();
        IsOperatorExample();
    }

    private static void DeclareAndAssign()
    {
        // <SnippetDeclaration>
        double? pi = 3.14;
        char? letter = 'a';

        int m2 = 10;
        int? m = m2;

        bool? flag = null;

        // An array of a nullable value type:
        int?[] arr = new int?[10];
        // </SnippetDeclaration>
    }

    private static void ExamineValueOfNullableType()
    {
        // <SnippetPatternMatching>
        int? a = 42;
        if (a is int valueOfA)
        {
            Console.WriteLine($"a is {valueOfA}");
        }
        else
        {
            Console.WriteLine("a does not have a value");
        }
        // Output:
        // a is 42
        // </SnippetPatternMatching>

        // <SnippetHasValue>
        int? b = 10;
        if (b.HasValue)
        {
            Console.WriteLine($"b is {b.Value}");
        }
        else
        {
            Console.WriteLine("b does not have a value");
        }
        // Output:
        // b is 10
        // </SnippetHasValue>

        // <SnippetCompareWithNull>
        int? c = 7;
        if (c != null)
        {
            Console.WriteLine($"c is {c.Value}");
        }
        else
        {
            Console.WriteLine("c does not have a value");
        }
        // Output:
        // c is 7
        // </SnippetCompareWithNull>
    }

    private static void UseNullCoalescingOperator()
    {
        // <SnippetNullCoalescing>
        int? a = 28;
        int b = a ?? -1;
        Console.WriteLine($"b is {b}");  // output: b is 28

        int? c = null;
        int d = c ?? -1;
        Console.WriteLine($"d is {d}");  // output: d is -1
        // </SnippetNullCoalescing>
    }

    private static void ExplicitCast()
    {
        // <SnippetCast>
        int? n = null;

        //int m1 = n;    // Doesn't compile
        int n2 = (int)n; // Compiles, but throws an exception if n is null
        // </SnippetCast>
    }

    private static void Operators()
    {
        // <SnippetLiftedOperator>
        int? a = 10;
        int? b = null;
        int? c = 10;

        a++;        // a is 11
        a = a * c;  // a is 110
        a = a + b;  // a is null
        // </SnippetLiftedOperator>
    }

    private static void ComparisonOperators()
    {
        // <SnippetComparisonOperators>
        int? a = 10;
        Console.WriteLine($"{a} >= null is {a >= null}");
        Console.WriteLine($"{a} < null is {a < null}");
        Console.WriteLine($"{a} == null is {a == null}");
        // Output:
        // 10 >= null is False
        // 10 < null is False
        // 10 == null is False

        int? b = null;
        int? c = null;
        Console.WriteLine($"null >= null is {b >= c}");
        Console.WriteLine($"null == null is {b == c}");
        // Output:
        // null >= null is False
        // null == null is True
        // </SnippetComparisonOperators>
    }

    private static void BoxingAndUnboxing()
    {
        // <SnippetBoxing>
        int a = 41;
        object aBoxed = a;
        int? aNullable = (int?)aBoxed;
        Console.WriteLine($"Value of aNullable: {aNullable}");

        object aNullableBoxed = aNullable;
        if (aNullableBoxed is int valueOfA)
        {
            Console.WriteLine($"aNullableBoxed is boxed int: {valueOfA}");
        }
        // Output:
        // Value of aNullable: 41
        // aNullableBoxed is boxed int: 41
        // </SnippetBoxing>
    }

    private static void WhetherTypeIsNullable()
    {
        // <SnippetIsTypeNullable>
        Console.WriteLine($"int? is {(IsNullable(typeof(int?)) ? "nullable" : "non nullable")} value type");
        Console.WriteLine($"int is {(IsNullable(typeof(int)) ? "nullable" : "non-nullable")} value type");

        bool IsNullable(Type type) => Nullable.GetUnderlyingType(type) != null;

        // Output:
        // int? is nullable value type
        // int is non-nullable value type
        // </SnippetIsTypeNullable>
    }

    private static void GetTypeExample()
    {
        // <SnippetGetType>
        int? a = 17;
        Type typeOfA = a.GetType();
        Console.WriteLine(typeOfA.FullName);
        // Output:
        // System.Int32
        // </SnippetGetType>
    }

    private static void IsOperatorExample()
    {
        // <SnippetIsOperator>
        int? a = 14;
        if (a is int)
        {
            Console.WriteLine("int? instance is compatible with int");
        }

        int b = 17;
        if (b is int?)
        {
            Console.WriteLine("int instance is compatible with int?");
        }
        // Output:
        // int? instance is compatible with int
        // int instance is compatible with int?
        // </SnippetIsOperator>
    }
}

As the example shows, you use the typeof operator to create a System.Type instance.

If you want to determine whether an instance is of a nullable value type, don't use the System.Object.GetType method to get a System.Type instance to test by using the preceding code. When you call the System.Object.GetType method on an instance of a nullable value type, the instance is boxed to System.Object. Because boxing a non-null instance of a nullable value type is equivalent to boxing a value of the underlying type, System.Object.GetType returns a System.Type instance that represents the underlying type of a nullable value type:

sample · csharpdocs/csharp/language-reference/builtin-types/snippets/shared/NullableValueTypes.csGetType

csharp
namespace builtin_types;

public static class NullableValueTypes
{
    public static void Examples()
    {
        DeclareAndAssign();
        ExamineValueOfNullableType();
        UseNullCoalescingOperator();
        Operators();
        ComparisonOperators();
        BoxingAndUnboxing();
        WhetherTypeIsNullable();
        GetTypeExample();
        IsOperatorExample();
    }

    private static void DeclareAndAssign()
    {
        // <SnippetDeclaration>
        double? pi = 3.14;
        char? letter = 'a';

        int m2 = 10;
        int? m = m2;

        bool? flag = null;

        // An array of a nullable value type:
        int?[] arr = new int?[10];
        // </SnippetDeclaration>
    }

    private static void ExamineValueOfNullableType()
    {
        // <SnippetPatternMatching>
        int? a = 42;
        if (a is int valueOfA)
        {
            Console.WriteLine($"a is {valueOfA}");
        }
        else
        {
            Console.WriteLine("a does not have a value");
        }
        // Output:
        // a is 42
        // </SnippetPatternMatching>

        // <SnippetHasValue>
        int? b = 10;
        if (b.HasValue)
        {
            Console.WriteLine($"b is {b.Value}");
        }
        else
        {
            Console.WriteLine("b does not have a value");
        }
        // Output:
        // b is 10
        // </SnippetHasValue>

        // <SnippetCompareWithNull>
        int? c = 7;
        if (c != null)
        {
            Console.WriteLine($"c is {c.Value}");
        }
        else
        {
            Console.WriteLine("c does not have a value");
        }
        // Output:
        // c is 7
        // </SnippetCompareWithNull>
    }

    private static void UseNullCoalescingOperator()
    {
        // <SnippetNullCoalescing>
        int? a = 28;
        int b = a ?? -1;
        Console.WriteLine($"b is {b}");  // output: b is 28

        int? c = null;
        int d = c ?? -1;
        Console.WriteLine($"d is {d}");  // output: d is -1
        // </SnippetNullCoalescing>
    }

    private static void ExplicitCast()
    {
        // <SnippetCast>
        int? n = null;

        //int m1 = n;    // Doesn't compile
        int n2 = (int)n; // Compiles, but throws an exception if n is null
        // </SnippetCast>
    }

    private static void Operators()
    {
        // <SnippetLiftedOperator>
        int? a = 10;
        int? b = null;
        int? c = 10;

        a++;        // a is 11
        a = a * c;  // a is 110
        a = a + b;  // a is null
        // </SnippetLiftedOperator>
    }

    private static void ComparisonOperators()
    {
        // <SnippetComparisonOperators>
        int? a = 10;
        Console.WriteLine($"{a} >= null is {a >= null}");
        Console.WriteLine($"{a} < null is {a < null}");
        Console.WriteLine($"{a} == null is {a == null}");
        // Output:
        // 10 >= null is False
        // 10 < null is False
        // 10 == null is False

        int? b = null;
        int? c = null;
        Console.WriteLine($"null >= null is {b >= c}");
        Console.WriteLine($"null == null is {b == c}");
        // Output:
        // null >= null is False
        // null == null is True
        // </SnippetComparisonOperators>
    }

    private static void BoxingAndUnboxing()
    {
        // <SnippetBoxing>
        int a = 41;
        object aBoxed = a;
        int? aNullable = (int?)aBoxed;
        Console.WriteLine($"Value of aNullable: {aNullable}");

        object aNullableBoxed = aNullable;
        if (aNullableBoxed is int valueOfA)
        {
            Console.WriteLine($"aNullableBoxed is boxed int: {valueOfA}");
        }
        // Output:
        // Value of aNullable: 41
        // aNullableBoxed is boxed int: 41
        // </SnippetBoxing>
    }

    private static void WhetherTypeIsNullable()
    {
        // <SnippetIsTypeNullable>
        Console.WriteLine($"int? is {(IsNullable(typeof(int?)) ? "nullable" : "non nullable")} value type");
        Console.WriteLine($"int is {(IsNullable(typeof(int)) ? "nullable" : "non-nullable")} value type");

        bool IsNullable(Type type) => Nullable.GetUnderlyingType(type) != null;

        // Output:
        // int? is nullable value type
        // int is non-nullable value type
        // </SnippetIsTypeNullable>
    }

    private static void GetTypeExample()
    {
        // <SnippetGetType>
        int? a = 17;
        Type typeOfA = a.GetType();
        Console.WriteLine(typeOfA.FullName);
        // Output:
        // System.Int32
        // </SnippetGetType>
    }

    private static void IsOperatorExample()
    {
        // <SnippetIsOperator>
        int? a = 14;
        if (a is int)
        {
            Console.WriteLine("int? instance is compatible with int");
        }

        int b = 17;
        if (b is int?)
        {
            Console.WriteLine("int instance is compatible with int?");
        }
        // Output:
        // int? instance is compatible with int
        // int instance is compatible with int?
        // </SnippetIsOperator>
    }
}

Also, don't use the is operator to determine whether an instance is of a nullable value type. As the following example shows, you can't distinguish types of a nullable value type instance and its underlying type instance by using the is operator:

sample · csharpdocs/csharp/language-reference/builtin-types/snippets/shared/NullableValueTypes.csIsOperator

csharp
namespace builtin_types;

public static class NullableValueTypes
{
    public static void Examples()
    {
        DeclareAndAssign();
        ExamineValueOfNullableType();
        UseNullCoalescingOperator();
        Operators();
        ComparisonOperators();
        BoxingAndUnboxing();
        WhetherTypeIsNullable();
        GetTypeExample();
        IsOperatorExample();
    }

    private static void DeclareAndAssign()
    {
        // <SnippetDeclaration>
        double? pi = 3.14;
        char? letter = 'a';

        int m2 = 10;
        int? m = m2;

        bool? flag = null;

        // An array of a nullable value type:
        int?[] arr = new int?[10];
        // </SnippetDeclaration>
    }

    private static void ExamineValueOfNullableType()
    {
        // <SnippetPatternMatching>
        int? a = 42;
        if (a is int valueOfA)
        {
            Console.WriteLine($"a is {valueOfA}");
        }
        else
        {
            Console.WriteLine("a does not have a value");
        }
        // Output:
        // a is 42
        // </SnippetPatternMatching>

        // <SnippetHasValue>
        int? b = 10;
        if (b.HasValue)
        {
            Console.WriteLine($"b is {b.Value}");
        }
        else
        {
            Console.WriteLine("b does not have a value");
        }
        // Output:
        // b is 10
        // </SnippetHasValue>

        // <SnippetCompareWithNull>
        int? c = 7;
        if (c != null)
        {
            Console.WriteLine($"c is {c.Value}");
        }
        else
        {
            Console.WriteLine("c does not have a value");
        }
        // Output:
        // c is 7
        // </SnippetCompareWithNull>
    }

    private static void UseNullCoalescingOperator()
    {
        // <SnippetNullCoalescing>
        int? a = 28;
        int b = a ?? -1;
        Console.WriteLine($"b is {b}");  // output: b is 28

        int? c = null;
        int d = c ?? -1;
        Console.WriteLine($"d is {d}");  // output: d is -1
        // </SnippetNullCoalescing>
    }

    private static void ExplicitCast()
    {
        // <SnippetCast>
        int? n = null;

        //int m1 = n;    // Doesn't compile
        int n2 = (int)n; // Compiles, but throws an exception if n is null
        // </SnippetCast>
    }

    private static void Operators()
    {
        // <SnippetLiftedOperator>
        int? a = 10;
        int? b = null;
        int? c = 10;

        a++;        // a is 11
        a = a * c;  // a is 110
        a = a + b;  // a is null
        // </SnippetLiftedOperator>
    }

    private static void ComparisonOperators()
    {
        // <SnippetComparisonOperators>
        int? a = 10;
        Console.WriteLine($"{a} >= null is {a >= null}");
        Console.WriteLine($"{a} < null is {a < null}");
        Console.WriteLine($"{a} == null is {a == null}");
        // Output:
        // 10 >= null is False
        // 10 < null is False
        // 10 == null is False

        int? b = null;
        int? c = null;
        Console.WriteLine($"null >= null is {b >= c}");
        Console.WriteLine($"null == null is {b == c}");
        // Output:
        // null >= null is False
        // null == null is True
        // </SnippetComparisonOperators>
    }

    private static void BoxingAndUnboxing()
    {
        // <SnippetBoxing>
        int a = 41;
        object aBoxed = a;
        int? aNullable = (int?)aBoxed;
        Console.WriteLine($"Value of aNullable: {aNullable}");

        object aNullableBoxed = aNullable;
        if (aNullableBoxed is int valueOfA)
        {
            Console.WriteLine($"aNullableBoxed is boxed int: {valueOfA}");
        }
        // Output:
        // Value of aNullable: 41
        // aNullableBoxed is boxed int: 41
        // </SnippetBoxing>
    }

    private static void WhetherTypeIsNullable()
    {
        // <SnippetIsTypeNullable>
        Console.WriteLine($"int? is {(IsNullable(typeof(int?)) ? "nullable" : "non nullable")} value type");
        Console.WriteLine($"int is {(IsNullable(typeof(int)) ? "nullable" : "non-nullable")} value type");

        bool IsNullable(Type type) => Nullable.GetUnderlyingType(type) != null;

        // Output:
        // int? is nullable value type
        // int is non-nullable value type
        // </SnippetIsTypeNullable>
    }

    private static void GetTypeExample()
    {
        // <SnippetGetType>
        int? a = 17;
        Type typeOfA = a.GetType();
        Console.WriteLine(typeOfA.FullName);
        // Output:
        // System.Int32
        // </SnippetGetType>
    }

    private static void IsOperatorExample()
    {
        // <SnippetIsOperator>
        int? a = 14;
        if (a is int)
        {
            Console.WriteLine("int? instance is compatible with int");
        }

        int b = 17;
        if (b is int?)
        {
            Console.WriteLine("int instance is compatible with int?");
        }
        // Output:
        // int? instance is compatible with int
        // int instance is compatible with int?
        // </SnippetIsOperator>
    }
}

Instead, use the System.Nullable.GetUnderlyingType method from the first example and the typeof operator to check if an instance is of a nullable value type.

C# language specification

For more information, see the following sections of the C# language specification:

See also