FirstOrDefault
Default Value
C# Programming
.NET Development
LINQ

FirstOrDefault Default value other than null

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

FirstOrDefault is a LINQ method that returns the first matching element, or the type default when no match exists. For reference types that default is usually null, and for numeric value types it is usually zero, which can be ambiguous. If you need a custom fallback value, there are several reliable patterns depending on your target .NET version.

Baseline Behavior of FirstOrDefault

The default return value depends on the element type.

csharp
1using System;
2using System.Linq;
3
4var nums = new[] { 2, 4, 6 };
5
6int foundEven = nums.FirstOrDefault(n => n % 2 == 0); // 2
7int missingOdd = nums.FirstOrDefault(n => n % 2 == 1); // 0
8
9Console.WriteLine(foundEven);
10Console.WriteLine(missingOdd);

For reference types:

csharp
var names = new[] { "Ava", "Mia" };
string? value = names.FirstOrDefault(n => n.StartsWith("Z"));
Console.WriteLine(value is null ? "null" : value);

This behavior is by design and is often useful, but not always what business logic needs.

Pattern 1: Null-Coalescing for Reference Types

For strings or objects, the shortest approach is null coalescing.

csharp
string result = names.FirstOrDefault(n => n.StartsWith("Z")) ?? "Unknown";
Console.WriteLine(result);

This is readable and works well when null is the only unwanted default.

Pattern 2: DefaultIfEmpty for Custom Fallbacks

DefaultIfEmpty inserts a custom element when a sequence is empty. Combined with filtering, it gives a custom fallback for no-match scenarios.

csharp
1var value = names
2    .Where(n => n.StartsWith("Z"))
3    .DefaultIfEmpty("Fallback")
4    .First();
5
6Console.WriteLine(value);

This pattern works for both reference and value types.

Pattern 3: Explicit Optional Result for Value Types

For value types, zero can be a valid value and not a clear missing signal. Return nullable or a wrapper result to preserve meaning.

csharp
1int? maybeOdd = nums
2    .Where(n => n % 2 == 1)
3    .Select(n => (int?)n)
4    .FirstOrDefault();
5
6if (maybeOdd is null)
7{
8    Console.WriteLine("No odd number found");
9}

Using nullable values avoids collision with real domain values.

Pattern 4: Extension Method for Team Consistency

If custom defaults are frequent, add an extension method with explicit fallback semantics.

csharp
1using System;
2using System.Collections.Generic;
3using System.Linq;
4
5public static class EnumerableExtensions
6{
7    public static T FirstOr<T>(this IEnumerable<T> source, Func<T, bool> predicate, T fallback)
8    {
9        if (source == null) throw new ArgumentNullException(nameof(source));
10        if (predicate == null) throw new ArgumentNullException(nameof(predicate));
11
12        foreach (var item in source)
13        {
14            if (predicate(item))
15            {
16                return item;
17            }
18        }
19
20        return fallback;
21    }
22}
23
24var score = new[] { 10, 20, 30 }.FirstOr(x => x > 100, -1);
25Console.WriteLine(score); // -1

This keeps fallback policy explicit at call sites.

Newer Overloads in Recent Frameworks

Recent .NET versions include overloads where you can pass a custom default value directly to FirstOrDefault. If your target runtime supports it, use it for clarity.

csharp
var custom = names.FirstOrDefault(n => n.StartsWith("Z"), "Fallback");
Console.WriteLine(custom);

If you support older frameworks, keep DefaultIfEmpty or extension methods for compatibility.

API Contract Considerations

When no-match is meaningful business information, returning fallback strings can hide that state. In service layers, consider returning a result object instead.

csharp
1public record LookupResult<T>(bool Found, T? Value);
2
3public static LookupResult<string> FindName(string[] data, string prefix)
4{
5    var match = data.FirstOrDefault(n => n.StartsWith(prefix));
6    return new LookupResult<string>(match != null, match);
7}

This avoids confusion between fallback text and real data.

Testing Missing-Value Logic

Add unit tests for both found and missing paths. Missing-value bugs often appear after refactors where fallback behavior changes silently.

csharp
// Pseudo xUnit style assertions
// Assert.Equal("Fallback", names.Where(...).DefaultIfEmpty("Fallback").First());
// Assert.Equal(-1, nums.FirstOr(n => n > 100, -1));

Tests should confirm behavior for empty sequences and non-empty sequences with no matches.

Common Pitfalls

  • Assuming FirstOrDefault always supports custom default overloads on all target frameworks.
  • Treating zero as no-match for value types when zero is valid business data.
  • Returning fallback strings from domain code where callers need explicit not-found semantics.
  • Forgetting null checks when using FirstOrDefault on reference types.
  • Copying fallback logic inline everywhere instead of centralizing for consistency.

Summary

  • 'FirstOrDefault returns type default when no match is found.'
  • Use null-coalescing or DefaultIfEmpty for custom fallback behavior.
  • Prefer nullable or explicit result models when missing state matters.
  • Use extension methods to keep fallback semantics consistent across codebase.
  • Validate not-found behavior with focused unit tests.

Course illustration
Course illustration

All Rights Reserved.