Understanding IEnumerable vs. IQueryable in C#

Introduction

In C#, both IEnumerable and IQueryable are fundamental interfaces for working with collections of data. While they share a common ancestor and similar names, they serve distinct purposes and are optimized for different scenarios. Understanding their differences is crucial for writing efficient and scalable applications.

IEnumerable: In-Memory Data Traversal

The IEnumerable interface is designed for iterating over collections that reside in memory, such as lists, arrays, or custom collections. It provides a standard way to access elements sequentially without exposing the underlying data structure.

At its core, IEnumerable defines a single method, GetEnumerator(), which returns an enumerator object. This enumerator is responsible for moving through the collection and providing access to each item.

When you use LINQ methods like Where or Select on an IEnumerable source, the operations are executed immediately in memory. The methods accept delegates (like Func<T, bool>) that are invoked for each element as it's processed.

Example: Custom In-Memory Collection

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

public class ProductCatalog : IEnumerable<string>
{
    private readonly List<string> _products = new List<string>
    {
        "Laptop", "Smartphone", "Tablet", "Monitor", "Keyboard"
    };

    public IEnumerator<string> GetEnumerator()
    {
        return _products.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

public class Program
{
    public static void Main()
    {
        var catalog = new ProductCatalog();

        // Using LINQ with IEnumerable: operations happen in memory
        var electronicItems = catalog.Where(item => item.Contains("phone") || item.Contains("phone"));

        Console.WriteLine("Electronic Items (IEnumerable):");
        foreach (var item in electronicItems)
        {
            Console.WriteLine(item);
        }
    }
}
</string></string></string></string>

IQueryable: Remote Data Querying

The IQueryable interface builds upon IEnumerable by adding support for query composition and translation. It's primarily used when the data source is external, such as a database, and you want to leverage the data source's query capabilities (e.g., SQL).

Unlike IEnumerable, IQueryable methods accept expression trees (like Expression<Func<T, bool>>) instead of delegates. An expression tree represents the code as data, which can then be analyzed and translated by a query provider (e.g., Entity Framework) into a native query language like SQL. This allows for significant performance optimizations, such as filtering data on the database server before it's sent to the application.

Queries on IQueryable sources are typically executed lazily, meaning the actual data retrieval happens only when the result are enumerated (e.g., in a foreach loop or when calling ToEnumerable() or ToList()).

Example: Simulating a Database Query

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

public class DatabaseContext
{
    public IQueryable<string> Products => new List<string>
    {
        "Laptop", "Smartphone", "Tablet", "Monitor", "Keyboard"
    }.AsQueryable();
}

public class Program
{
    public static void Main()
    {
        var context = new DatabaseContext();

        // Using LINQ with IQueryable: query is translated to SQL-like logic
        var electronicItems = context.Products.Where(item => item.Contains("phone"));

        Console.WriteLine("Electronic Items (IQueryable - translated to SQL):");
        // The actual filtering happens here, potentially on a remote server
        foreach (var item in electronicItems)
        {
            Console.WriteLine(item);
        }
    }
}
</string></string>

Key Differences

  • Data Source: IEnumerable is for in-memory collections. IQueryable is for remote data sources (databases, web services).
  • Execution: IEnumerable executes queries immediately in memory. IQueryable uses deferred execution, translating the query for remote execution.
  • Performance: For small, local data, IEnumerable can be faster due to lower overhead. For large datasets or remote sources, IQueryable is superior as it minimizes data transfer and leverages server-side processing.
  • Method Parameters: IEnumerable LINQ methods use delegates (Func<T, TResult>). IQueryable LINQ methods use expression trees (Expression<Func<T, TResult>>).

Tags: csharp LINQ IEnumerable IQueryable .NET

Posted on Fri, 05 Jun 2026 17:26:13 +0000 by Dave2711