How to Use DefaultIfEmpty in C# LINQ

In C# LINQ, querying empty collections with methods like First, Single, or Average often throws an InvalidOperationException. The DefaultIfEmpty extension method solves this by providing a fallback element if the source sequence contains no items. This article explains how DefaultIfEmpty works, how to specify custom fallback values, and how to use it to implement left outer joins in LINQ queries.

What is DefaultIfEmpty?

The DefaultIfEmpty method checks whether an IEnumerable<T> sequence contains any elements. If the sequence has data, it returns the original sequence untouched. If the sequence is empty, it returns a new sequence containing a single element representing a default value.

Basic Usage with Type Defaults

When called without parameters, DefaultIfEmpty() supplies default(T) for the underlying type—0 for numeric types, false for booleans, and null for reference types.

using System;
using System.Collections.Generic;
using System.Linq;

List<int> numbers = new List<int>();

// numbers is empty, so DefaultIfEmpty() yields a sequence with one element: 0
int firstNumber = numbers.DefaultIfEmpty().First();

Console.WriteLine(firstNumber); // Output: 0

Without DefaultIfEmpty(), calling numbers.First() on an empty collection would throw an InvalidOperationException.

Providing a Custom Default Value

You can specify a meaningful fallback value by passing an argument to the method overload DefaultIfEmpty(TDefault).

List<string> names = new List<string>();

// Returns "Unknown" instead of the default null
string displayName = names.DefaultIfEmpty("Unknown").First();

Console.WriteLine(displayName); // Output: Unknown

This is particularly useful when working with user interfaces, logs, or reporting where null or 0 does not provide enough context.

Safe Aggregations

Certain LINQ aggregation methods, such as Average(), fail when invoked on empty sets. Using DefaultIfEmpty ensures these calculations execute without throwing exceptions:

List<double> scores = new List<double>();

// Avoids exception by averaging a sequence of [0.0]
double averageScore = scores.DefaultIfEmpty(0.0).Average();

Console.WriteLine(averageScore); // Output: 0

Performing Left Outer Joins

In relational database operations, a left outer join returns all rows from the primary table regardless of whether there is a matching row in the secondary table. In LINQ, this is achieved by combining GroupJoin with SelectMany and DefaultIfEmpty:

var departments = new[]
{
    new { Id = 1, Name = "Engineering" },
    new { Id = 2, Name = "Marketing" }
};

var employees = new[]
{
    new { Name = "Alice", DepartmentId = 1 }
};

var leftJoinQuery = from dept in departments
                    join emp in employees on dept.Id equals emp.DepartmentId into empGroup
                    from emp in empGroup.DefaultIfEmpty()
                    select new
                    {
                        Department = dept.Name,
                        Employee = emp != null ? emp.Name : "No Employee"
                    };

foreach (var item in leftJoinQuery)
{
    Console.WriteLine($"{item.Department}: {item.Employee}");
}
// Output:
// Engineering: Alice
// Marketing: No Employee

By calling empGroup.DefaultIfEmpty(), the query ensures departments without any matching employees still generate a result row where emp is set to null.