C#
IEnumerable
Null Check
Programming Tips
.NET

How to check if IEnumerable is null or empty?

Master System Design with Codemia

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

Introduction

Checking whether an IEnumerable is null or empty sounds simple, but there is one detail that matters: IEnumerable may represent a lazy sequence, not a materialized collection. That means a null-or-empty check can sometimes trigger enumeration.

The most common practical answer is source == null || !source.Any(). It is concise, readable, and efficient for most cases because Any() stops as soon as it finds the first element.

The Basic Check

For an IEnumerable<T>, the usual pattern is:

csharp
1using System.Collections.Generic;
2using System.Linq;
3
4IEnumerable<int> numbers = GetNumbers();
5
6bool isNullOrEmpty = numbers == null || !numbers.Any();

This works because:

  • the null check protects against NullReferenceException
  • 'Any() returns quickly once it sees the first item'
  • the code reads almost like plain English

For most application code, this is all you need.

A Reusable Extension Method

If you perform this check often, wrap it in an extension method:

csharp
1using System.Collections.Generic;
2using System.Linq;
3
4public static class EnumerableExtensions
5{
6    public static bool IsNullOrEmpty<T>(this IEnumerable<T>? source)
7    {
8        return source == null || !source.Any();
9    }
10}

Usage:

csharp
1if (numbers.IsNullOrEmpty())
2{
3    Console.WriteLine("No values available");
4}

This improves readability, especially in business logic where the null-or-empty concept appears frequently.

Be Aware of Lazy Sequences

IEnumerable<T> is not always a list. It might be a LINQ query, a generator, or a streaming sequence.

That means Any() may execute part of the query. For example:

csharp
1IEnumerable<int> values = Enumerable.Range(1, 10)
2    .Where(x =>
3    {
4        Console.WriteLine($"Testing {x}");
5        return x > 5;
6    });
7
8bool empty = !values.Any();

The query does not run until Any() is called. Then it starts enumerating until it finds a matching item or reaches the end.

That behavior is usually fine, but it is important to remember that checking emptiness can still have cost or side effects if the sequence is expensive or stateful.

Fast Path for Real Collections

Sometimes you want to avoid even a single-element enumeration when the source is already a collection with a count.

csharp
1using System.Collections.Generic;
2using System.Linq;
3
4public static class EnumerableExtensions
5{
6    public static bool IsNullOrEmpty<T>(this IEnumerable<T>? source)
7    {
8        if (source == null)
9            return true;
10
11        if (source is ICollection<T> collection)
12            return collection.Count == 0;
13
14        return !source.Any();
15    }
16}

This is a nice refinement because it uses Count directly for collections and falls back to Any() for everything else.

When Not to Check Twice

A common anti-pattern is:

csharp
1if (numbers != null && numbers.Any())
2{
3    foreach (var n in numbers)
4    {
5        Console.WriteLine(n);
6    }
7}

For a lazy sequence, this can enumerate twice: once for Any() and once for foreach. If the sequence is expensive, consider materializing it first or restructuring the code.

That is one reason IEnumerable<T> is different from List<T>. The abstraction is powerful, but it hides whether the sequence is cached or recomputed.

Common Pitfalls

The biggest mistake is calling Any() without a null check. Any() itself does not handle null sources.

Another common issue is assuming every IEnumerable<T> is a cheap in-memory collection. Some are queries, streams, or one-shot iterators.

A third problem is checking Any() and then enumerating again without realizing the sequence may run twice.

Summary

  • The usual check is source == null || !source.Any().
  • 'Any() is efficient because it stops after the first item.'
  • An extension method can make the pattern easier to reuse.
  • For real collections, a Count fast path can avoid enumeration.
  • Be careful with lazy sequences, because even an emptiness check may execute part of the query.

Course illustration
Course illustration

All Rights Reserved.