C# Default Literal for Type Initialization
This article provides an overview of the default literal
introduced in C# 7.1, explaining how it functions as a simplified
mechanism for type initialization. You will learn how the compiler
infers the appropriate default values for value types, reference types,
and generics, the specific syntactic contexts where it can be applied,
and the scenarios where explicit type specification remains
necessary.
Understanding the
default Literal
In earlier versions of C#, initializing a variable to its default
state required the default(T) operator, explicitly passing
the type name (for example, default(int) or
default(CancellationToken)). Starting with C# 7.1, the
language introduced the default literal, which allows you
to omit the redundant type argument whenever the compiler can infer the
target type from the context.
Under the hood, the default literal produces the exact
same result as default(T):
- Value Types: Numeric types evaluate to
0,boolevaluates tofalse,charevaluates to\0, and customstructinstances have all their fields recursively set to their respective default values. - Nullable Value Types
(
Nullable<T>): Evaluates to an instance whereHasValueisfalse(effectivelynull). - Reference Types: Any class, interface, delegate, or
array evaluates to
null. - Generic Type Parameters: Dynamically resolves to
either zero-initialized memory or
null, depending on whether the concrete type parameter substituted at runtime is a value type or a reference type.
Common Usage Scenarios
The compiler allows the default literal anywhere a
target type is already established:
1. Variable Declarations and Assignments
When declaring a variable with an explicit type, default
eliminates the need to repeat the type name:
int counter = default; // Evaluates to 0
string message = default; // Evaluates to null
Guid identifier = default; // Evaluates to Guid.Empty
DateTime timestamp = default; // Evaluates to DateTime.MinValue
KeyValuePair<int, string> pair = default; // Evaluates to [0, null]2. Method Optional Parameters
The default literal is widely used to provide default
values for optional arguments, particularly for structs like
CancellationToken:
public async Task FetchDataAsync(string url, CancellationToken cancellationToken = default)
{
// If not supplied by the caller, cancellationToken is initialized to CancellationToken.None
}3. Return Statements
When a method return type is explicitly declared,
default provides a clean exit without referencing the
type:
public Point GetCoordinates()
{
if (!TryCompute(out Point pt))
{
return default; // Returns new Point(0, 0)
}
return pt;
}4. Pattern Matching and Conditionals
You can compare values against default or use it in
ternary expressions where the target type is unambiguous:
// Checking if a struct equals its default state
if (timestamp == default)
{
// Handle uninitialized date
}
// Ternary assignment
string status = isValid ? "Success" : default;Limitations and Compiler Errors
Because the default literal relies entirely on target
typing, it cannot be used in contexts where the compiler cannot deduce
the expected type:
- Implicitly Typed Variables (
var): Writingvar x = default;causes a compilation error (CS0815) because the compiler cannot infer what typexshould be. In this situation, either use an explicit type (int x = default;) or use the older operator syntax (var x = default(int);). - Untyped Expressions: You cannot pass
defaultdirectly into an overloaded method or an unconstrained generic argument if the compiler has no way to determine which overload or type to select.