choices
decisions
options
maximum
default

Max or Default?

Master System Design with Codemia

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

Introduction

In many programs, you want the maximum value from a sequence, but only if the sequence is not empty. The catch is that methods such as LINQ Max() can throw on empty sequences for value types, so the real problem becomes how to return a sensible default without hiding bugs.

What Max() does on an empty sequence

For non-nullable numeric sequences, LINQ Max() throws InvalidOperationException if there are no elements:

csharp
1using System;
2using System.Linq;
3
4var values = Array.Empty<int>();
5Console.WriteLine(values.Max());

That behavior is correct from LINQ’s point of view because there is no mathematical maximum of an empty set. But application code often needs a fallback value anyway, especially when empty input is a normal state rather than an error.

The simplest pattern: DefaultIfEmpty

If a reasonable fallback exists, prepend it only when the sequence is empty:

csharp
1using System;
2using System.Linq;
3
4var values = Array.Empty<int>();
5int max = values.DefaultIfEmpty(0).Max();
6
7Console.WriteLine(max);

This is the common "max or default" pattern in LINQ. If values has elements, Max() returns the true maximum. If it is empty, the inserted default is the only element and becomes the result.

Choosing the right default

The default should come from domain rules, not habit. 0 might be fine for counts, but it can be misleading for temperatures, scores, or financial values where 0 looks like a real measurement.

For example:

csharp
1using System;
2using System.Linq;
3
4var scores = Array.Empty<int>();
5int bestScore = scores.DefaultIfEmpty(-1).Max();
6
7Console.WriteLine(bestScore);

Here, -1 is a better sentinel because it clearly means "no score yet" instead of "the maximum score was zero." In business code, that choice is often more important than the LINQ syntax itself.

Creating a reusable MaxOrDefault extension

If this pattern appears in many places, wrap it in an extension method:

csharp
1using System;
2using System.Collections.Generic;
3
4public static class EnumerableExtensions
5{
6    public static T MaxOrDefault<T>(this IEnumerable<T> source, T defaultValue)
7    {
8        using var enumerator = source.GetEnumerator();
9
10        if (!enumerator.MoveNext())
11        {
12            return defaultValue;
13        }
14
15        var comparer = Comparer<T>.Default;
16        T currentMax = enumerator.Current;
17
18        while (enumerator.MoveNext())
19        {
20            if (comparer.Compare(enumerator.Current, currentMax) > 0)
21            {
22                currentMax = enumerator.Current;
23            }
24        }
25
26        return currentMax;
27    }
28}

This makes the call site clearer and avoids repeating DefaultIfEmpty(...).Max() everywhere. It also makes the choice of default explicit, which is often the most important part of the design.

For projected values

Sometimes the sequence holds objects and you only care about one property. In that case, project first and then apply the same pattern:

csharp
1using System;
2using System.Linq;
3
4var people = new[]
5{
6    new { Name = "Ana", Score = 7 },
7    new { Name = "Ben", Score = 12 }
8};
9
10int maxScore = people.Select(p => p.Score).DefaultIfEmpty(0).Max();
11Console.WriteLine(maxScore);

This keeps the fallback logic explicit instead of burying it in object handling code. The same idea works for nullable values too, but then you should decide whether null itself is the right default.

Common Pitfalls

  • Using Max() directly and forgetting it throws on empty sequences.
  • Picking a default value that looks like a valid real result and hides missing data.
  • Treating 0 as a universal fallback when the domain needs a more meaningful sentinel.
  • Repeating the same fallback logic throughout the codebase instead of centralizing it.

Summary

  • 'Max() throws on empty value-type sequences, so you need a fallback strategy when emptiness is valid.'
  • 'DefaultIfEmpty(fallback).Max() is the simplest LINQ pattern for "max or default."'
  • The fallback value should reflect domain meaning, not just convenience.
  • A reusable extension method can make the intent clearer across a codebase.

Course illustration
Course illustration

All Rights Reserved.