Explain the difference between IEnumerable, ICollection, IList, and IQueryable.

10 minintermediate.NETcollectionsLINQ

Quick Answer

IEnumerable provides basic iteration (forward-only, read-only). ICollection adds Count and Add/Remove operations. IList adds index-based access and insertion. IQueryable is for database queries with deferred execution and expression trees. Use IEnumerable for simple sequences, ICollection for collections with count, IList for indexed access, and IQueryable for database operations.

Detailed Answer

These are all collection interfaces with different capabilities and use cases.

IEnumerable<T>:

  • Most basic interface for iteration
  • Forward-only, read-only iteration
  • Deferred execution (lazy evaluation)
  • In-memory collections
  • Methods: GetEnumerator()
IEnumerable<int> numbers = new List<int> { 1, 2, 3, 4, 5 };
foreach (var num in numbers)  // Can only iterate forward
{
    Console.WriteLine(num);
}
// Cannot: Add, Remove, Count, or Index access

ICollection<T>:

  • Extends IEnumerable<T>
  • Adds Count property
  • Allows Add, Remove, Clear operations
  • Still no index-based access
  • Methods: Add(), Remove(), Clear(), Contains()
  • Properties: Count, IsReadOnly
ICollection<int> numbers = new List<int>();
numbers.Add(1);
numbers.Remove(1);
int count = numbers.Count;  // Can get count
// Cannot: Index access like numbers[0]

IList<T>:

  • Extends ICollection<T> (and IEnumerable<T>)
  • Adds index-based access
  • Allows insertion at specific positions
  • Most feature-rich in-memory collection interface
  • Methods: Insert(), RemoveAt(), IndexOf()
  • Properties: Indexer [int index]
IList<int> numbers = new List<int> { 1, 2, 3 };
numbers[0] = 10;  // Index-based access
numbers.Insert(1, 5);  // Insert at position
int value = numbers[2];  // Read by index

IQueryable<T>:

  • Extends IEnumerable<T>
  • Designed for out-of-memory data sources (databases)
  • Expression trees for query translation
  • Deferred execution
  • Queries translated to native query language (SQL, etc.)
  • Used with LINQ to SQL, Entity Framework
// IQueryable - Query executed on DATABASE
IQueryable<User> query = dbContext.Users
    .Where(u => u.Age > 18)  // Translated to SQL WHERE clause
    .OrderBy(u => u.Name);   // Translated to SQL ORDER BY

// IEnumerable - Query executed in MEMORY
IEnumerable<User> query = dbContext.Users.ToList()  // Loads all data first!
    .Where(u => u.Age > 18)  // Filters in C# memory
    .OrderBy(u => u.Name);   // Sorts in C# memory

Comparison Table:

FeatureIEnumerableICollectionIListIQueryable
Iteration
Count
Add/Remove
Index Access
Database Query
Deferred Execution

Performance Implications:

// BAD - Loads all users into memory, then filters
IEnumerable<User> users = dbContext.Users.ToList()
    .Where(u => u.Age > 18);

// GOOD - Filters in database, loads only matching records
IQueryable<User> users = dbContext.Users
    .Where(u => u.Age > 18);

Best Practices:

  • Return IEnumerable<T> for simple read-only sequences
  • Use ICollection<T> when you need Count and Add/Remove
  • Use IList<T> when you need index-based access
  • Use IQueryable<T> for database queries to leverage server-side filtering

Related Resources