The null-coalescing operator ?? returns the value of its left-hand operand if it's not null. Otherwise, it evaluates the right-hand operand and returns its result. The ?? operator doesn't evaluate its right-hand operand if the left-hand operand evaluates to non-null. The null-coalescing assignment operator ??= assigns the value of its right-hand operand to its left-hand operand only if the left-hand operand evaluates to null. The ??= operator doesn't evaluate its right-hand operand if the left-hand operand evaluates to non-null.
sample · csharpdocs/csharp/language-reference/operators/snippets/shared/NullCoalescingOperator.csAssignment
using System.Diagnostics.CodeAnalysis;
namespace operators;
public static class NullCoalescingOperator
{
public static void Examples()
{
WithNullConditional();
WithNullableTypes();
NullCoalescingAssignment();
}
public record class Human(string FirstName, string LastName)
{
public string FirstName { get; set; } = FirstName;
}
public static void AddMessageAtIndex()
{
List<string> messages = new List<string>(10);
Human person = new Human("First", "Last");
// <NullForgivingAssignment>
person?.FirstName = "Scott";
messages?[5] = "five";
// </NullForgivingAssignment>
int index = 0;
int[] values = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
// <ConditionalRHS>
values?[2] = GenerateNextIndex();
int GenerateNextIndex() => index++;
// </ConditionalRHS>
// <EquivalentIfStatement>
if (values is not null)
{
values[2] = GenerateNextIndex();
}
// </EquivalentIfStatement>
}
private static void WithNullConditional()
{
// <SnippetWithNullConditional>
double SumNumbers(List<double[]> setsOfNumbers, int indexOfSetToSum)
{
return setsOfNumbers?[indexOfSetToSum]?.Sum() ?? double.NaN;
}
var sum = SumNumbers(null, 0);
Console.WriteLine(sum); // output: NaN
// </SnippetWithNullConditional>
}
private static void WithNullableTypes()
{
// <SnippetWithNullableTypes>
int? a = null;
int b = a ?? -1;
Console.WriteLine(b); // output: -1
// </SnippetWithNullableTypes>
}
private class Person
{
string name;
// <SnippetWithThrowExpression>
public string Name
{
get => name;
set => name = value ?? throw new ArgumentNullException(nameof(value), "Name cannot be null");
}
// </SnippetWithThrowExpression>
}
// <SnippetUnconstrainedType>
private static void Display<T>(T a, T backup)
{
Console.WriteLine(a ?? backup);
}
// </SnippetUnconstrainedType>
private static void NullCoalescingAssignment()
{
// <SnippetAssignment>
List<int>? numbers = null;
int? a = null;
Console.WriteLine((numbers is null)); // expected: true
// if numbers is null, initialize it. Then, add 5 to numbers
(numbers ??= new List<int>()).Add(5);
Console.WriteLine(string.Join(" ", numbers)); // output: 5
Console.WriteLine((numbers is null)); // expected: false
Console.WriteLine((a is null)); // expected: true
Console.WriteLine((a ?? 3)); // expected: 3 since a is still null
// if a is null then assign 0 to a and add a to the list
numbers.Add(a ??= 0);
Console.WriteLine((a is null)); // expected: false
Console.WriteLine(string.Join(" ", numbers)); // output: 5 0
Console.WriteLine(a); // output: 0
// </SnippetAssignment>
}
}The left-hand operand of the ??= operator must be a variable, a property, or an indexer element.
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.
The type of the left-hand operand of the ?? and ??= operators can't be a non-nullable value type. In particular, you can use the null-coalescing operators with unconstrained type parameters:
sample · csharpdocs/csharp/language-reference/operators/snippets/shared/NullCoalescingOperator.csUnconstrainedType
using System.Diagnostics.CodeAnalysis;
namespace operators;
public static class NullCoalescingOperator
{
public static void Examples()
{
WithNullConditional();
WithNullableTypes();
NullCoalescingAssignment();
}
public record class Human(string FirstName, string LastName)
{
public string FirstName { get; set; } = FirstName;
}
public static void AddMessageAtIndex()
{
List<string> messages = new List<string>(10);
Human person = new Human("First", "Last");
// <NullForgivingAssignment>
person?.FirstName = "Scott";
messages?[5] = "five";
// </NullForgivingAssignment>
int index = 0;
int[] values = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
// <ConditionalRHS>
values?[2] = GenerateNextIndex();
int GenerateNextIndex() => index++;
// </ConditionalRHS>
// <EquivalentIfStatement>
if (values is not null)
{
values[2] = GenerateNextIndex();
}
// </EquivalentIfStatement>
}
private static void WithNullConditional()
{
// <SnippetWithNullConditional>
double SumNumbers(List<double[]> setsOfNumbers, int indexOfSetToSum)
{
return setsOfNumbers?[indexOfSetToSum]?.Sum() ?? double.NaN;
}
var sum = SumNumbers(null, 0);
Console.WriteLine(sum); // output: NaN
// </SnippetWithNullConditional>
}
private static void WithNullableTypes()
{
// <SnippetWithNullableTypes>
int? a = null;
int b = a ?? -1;
Console.WriteLine(b); // output: -1
// </SnippetWithNullableTypes>
}
private class Person
{
string name;
// <SnippetWithThrowExpression>
public string Name
{
get => name;
set => name = value ?? throw new ArgumentNullException(nameof(value), "Name cannot be null");
}
// </SnippetWithThrowExpression>
}
// <SnippetUnconstrainedType>
private static void Display<T>(T a, T backup)
{
Console.WriteLine(a ?? backup);
}
// </SnippetUnconstrainedType>
private static void NullCoalescingAssignment()
{
// <SnippetAssignment>
List<int>? numbers = null;
int? a = null;
Console.WriteLine((numbers is null)); // expected: true
// if numbers is null, initialize it. Then, add 5 to numbers
(numbers ??= new List<int>()).Add(5);
Console.WriteLine(string.Join(" ", numbers)); // output: 5
Console.WriteLine((numbers is null)); // expected: false
Console.WriteLine((a is null)); // expected: true
Console.WriteLine((a ?? 3)); // expected: 3 since a is still null
// if a is null then assign 0 to a and add a to the list
numbers.Add(a ??= 0);
Console.WriteLine((a is null)); // expected: false
Console.WriteLine(string.Join(" ", numbers)); // output: 5 0
Console.WriteLine(a); // output: 0
// </SnippetAssignment>
}
}The null-coalescing operators are right-associative. That is, expressions of the form
a ?? b ?? c
d ??= e ??= fare evaluated as
a ?? (b ?? c)
d ??= (e ??= f)Examples
The ?? and ??= operators are useful in the following scenarios:
- In expressions that use the null-conditional operators
?.and?[], use the??operator to provide an alternative expression to evaluate if the result of the expression with null-conditional operations isnull:
sample · csharpdocs/csharp/language-reference/operators/snippets/shared/NullCoalescingOperator.csWithNullConditional
using System.Diagnostics.CodeAnalysis;
namespace operators;
public static class NullCoalescingOperator
{
public static void Examples()
{
WithNullConditional();
WithNullableTypes();
NullCoalescingAssignment();
}
public record class Human(string FirstName, string LastName)
{
public string FirstName { get; set; } = FirstName;
}
public static void AddMessageAtIndex()
{
List<string> messages = new List<string>(10);
Human person = new Human("First", "Last");
// <NullForgivingAssignment>
person?.FirstName = "Scott";
messages?[5] = "five";
// </NullForgivingAssignment>
int index = 0;
int[] values = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
// <ConditionalRHS>
values?[2] = GenerateNextIndex();
int GenerateNextIndex() => index++;
// </ConditionalRHS>
// <EquivalentIfStatement>
if (values is not null)
{
values[2] = GenerateNextIndex();
}
// </EquivalentIfStatement>
}
private static void WithNullConditional()
{
// <SnippetWithNullConditional>
double SumNumbers(List<double[]> setsOfNumbers, int indexOfSetToSum)
{
return setsOfNumbers?[indexOfSetToSum]?.Sum() ?? double.NaN;
}
var sum = SumNumbers(null, 0);
Console.WriteLine(sum); // output: NaN
// </SnippetWithNullConditional>
}
private static void WithNullableTypes()
{
// <SnippetWithNullableTypes>
int? a = null;
int b = a ?? -1;
Console.WriteLine(b); // output: -1
// </SnippetWithNullableTypes>
}
private class Person
{
string name;
// <SnippetWithThrowExpression>
public string Name
{
get => name;
set => name = value ?? throw new ArgumentNullException(nameof(value), "Name cannot be null");
}
// </SnippetWithThrowExpression>
}
// <SnippetUnconstrainedType>
private static void Display<T>(T a, T backup)
{
Console.WriteLine(a ?? backup);
}
// </SnippetUnconstrainedType>
private static void NullCoalescingAssignment()
{
// <SnippetAssignment>
List<int>? numbers = null;
int? a = null;
Console.WriteLine((numbers is null)); // expected: true
// if numbers is null, initialize it. Then, add 5 to numbers
(numbers ??= new List<int>()).Add(5);
Console.WriteLine(string.Join(" ", numbers)); // output: 5
Console.WriteLine((numbers is null)); // expected: false
Console.WriteLine((a is null)); // expected: true
Console.WriteLine((a ?? 3)); // expected: 3 since a is still null
// if a is null then assign 0 to a and add a to the list
numbers.Add(a ??= 0);
Console.WriteLine((a is null)); // expected: false
Console.WriteLine(string.Join(" ", numbers)); // output: 5 0
Console.WriteLine(a); // output: 0
// </SnippetAssignment>
}
}- When you work with nullable value types and need to provide a value of an underlying value type, use the
??operator to specify the value to provide if a nullable type value isnull:
sample · csharpdocs/csharp/language-reference/operators/snippets/shared/NullCoalescingOperator.csWithNullableTypes
using System.Diagnostics.CodeAnalysis;
namespace operators;
public static class NullCoalescingOperator
{
public static void Examples()
{
WithNullConditional();
WithNullableTypes();
NullCoalescingAssignment();
}
public record class Human(string FirstName, string LastName)
{
public string FirstName { get; set; } = FirstName;
}
public static void AddMessageAtIndex()
{
List<string> messages = new List<string>(10);
Human person = new Human("First", "Last");
// <NullForgivingAssignment>
person?.FirstName = "Scott";
messages?[5] = "five";
// </NullForgivingAssignment>
int index = 0;
int[] values = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
// <ConditionalRHS>
values?[2] = GenerateNextIndex();
int GenerateNextIndex() => index++;
// </ConditionalRHS>
// <EquivalentIfStatement>
if (values is not null)
{
values[2] = GenerateNextIndex();
}
// </EquivalentIfStatement>
}
private static void WithNullConditional()
{
// <SnippetWithNullConditional>
double SumNumbers(List<double[]> setsOfNumbers, int indexOfSetToSum)
{
return setsOfNumbers?[indexOfSetToSum]?.Sum() ?? double.NaN;
}
var sum = SumNumbers(null, 0);
Console.WriteLine(sum); // output: NaN
// </SnippetWithNullConditional>
}
private static void WithNullableTypes()
{
// <SnippetWithNullableTypes>
int? a = null;
int b = a ?? -1;
Console.WriteLine(b); // output: -1
// </SnippetWithNullableTypes>
}
private class Person
{
string name;
// <SnippetWithThrowExpression>
public string Name
{
get => name;
set => name = value ?? throw new ArgumentNullException(nameof(value), "Name cannot be null");
}
// </SnippetWithThrowExpression>
}
// <SnippetUnconstrainedType>
private static void Display<T>(T a, T backup)
{
Console.WriteLine(a ?? backup);
}
// </SnippetUnconstrainedType>
private static void NullCoalescingAssignment()
{
// <SnippetAssignment>
List<int>? numbers = null;
int? a = null;
Console.WriteLine((numbers is null)); // expected: true
// if numbers is null, initialize it. Then, add 5 to numbers
(numbers ??= new List<int>()).Add(5);
Console.WriteLine(string.Join(" ", numbers)); // output: 5
Console.WriteLine((numbers is null)); // expected: false
Console.WriteLine((a is null)); // expected: true
Console.WriteLine((a ?? 3)); // expected: 3 since a is still null
// if a is null then assign 0 to a and add a to the list
numbers.Add(a ??= 0);
Console.WriteLine((a is null)); // expected: false
Console.WriteLine(string.Join(" ", numbers)); // output: 5 0
Console.WriteLine(a); // output: 0
// </SnippetAssignment>
}
} Use the System.Nullable.GetValueOrDefault method if the value to use when a nullable type value is null should be the default value of the underlying value type.
- To make the argument-checking code more concise, use a
throwexpression as the right-hand operand of the??operator:
sample · csharpdocs/csharp/language-reference/operators/snippets/shared/NullCoalescingOperator.csWithThrowExpression
using System.Diagnostics.CodeAnalysis;
namespace operators;
public static class NullCoalescingOperator
{
public static void Examples()
{
WithNullConditional();
WithNullableTypes();
NullCoalescingAssignment();
}
public record class Human(string FirstName, string LastName)
{
public string FirstName { get; set; } = FirstName;
}
public static void AddMessageAtIndex()
{
List<string> messages = new List<string>(10);
Human person = new Human("First", "Last");
// <NullForgivingAssignment>
person?.FirstName = "Scott";
messages?[5] = "five";
// </NullForgivingAssignment>
int index = 0;
int[] values = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
// <ConditionalRHS>
values?[2] = GenerateNextIndex();
int GenerateNextIndex() => index++;
// </ConditionalRHS>
// <EquivalentIfStatement>
if (values is not null)
{
values[2] = GenerateNextIndex();
}
// </EquivalentIfStatement>
}
private static void WithNullConditional()
{
// <SnippetWithNullConditional>
double SumNumbers(List<double[]> setsOfNumbers, int indexOfSetToSum)
{
return setsOfNumbers?[indexOfSetToSum]?.Sum() ?? double.NaN;
}
var sum = SumNumbers(null, 0);
Console.WriteLine(sum); // output: NaN
// </SnippetWithNullConditional>
}
private static void WithNullableTypes()
{
// <SnippetWithNullableTypes>
int? a = null;
int b = a ?? -1;
Console.WriteLine(b); // output: -1
// </SnippetWithNullableTypes>
}
private class Person
{
string name;
// <SnippetWithThrowExpression>
public string Name
{
get => name;
set => name = value ?? throw new ArgumentNullException(nameof(value), "Name cannot be null");
}
// </SnippetWithThrowExpression>
}
// <SnippetUnconstrainedType>
private static void Display<T>(T a, T backup)
{
Console.WriteLine(a ?? backup);
}
// </SnippetUnconstrainedType>
private static void NullCoalescingAssignment()
{
// <SnippetAssignment>
List<int>? numbers = null;
int? a = null;
Console.WriteLine((numbers is null)); // expected: true
// if numbers is null, initialize it. Then, add 5 to numbers
(numbers ??= new List<int>()).Add(5);
Console.WriteLine(string.Join(" ", numbers)); // output: 5
Console.WriteLine((numbers is null)); // expected: false
Console.WriteLine((a is null)); // expected: true
Console.WriteLine((a ?? 3)); // expected: 3 since a is still null
// if a is null then assign 0 to a and add a to the list
numbers.Add(a ??= 0);
Console.WriteLine((a is null)); // expected: false
Console.WriteLine(string.Join(" ", numbers)); // output: 5 0
Console.WriteLine(a); // output: 0
// </SnippetAssignment>
}
}The preceding example also demonstrates how to use expression-bodied members to define a property.
Use the
??=operator to replace code of the following form:if (variable is null) { variable = expression; }Use the following code:
variable ??= expression;
Operator overloadability
You can't overload the ?? and ??= operators.
C# language specification
For more information about the ?? operator, see The null coalescing operator section of the C# language specification.
For more information about the ??= operator, see the Compound assignment section of the C# language specification.