If you’re working in C# and .NET, you’ll quickly run into different collection interfaces like IEnumerable<T>
and IReadOnlyList<T>
. At first glance, they seem pretty similar—both let you work with a set of items in a read-only way. So when should you use each? In this post, I’ll break down the differences, show real-world use cases, and give you code samples so you know exactly when to reach for one or the other.
Quick Definitions
IEnumerable<T>
The simplest way to represent a sequence of items that you can iterate over (with foreach). It doesn’t let you access by index or get the count directly.
IReadOnlyList<T>
Represents a read-only list of items. You can access items by index and get the number of items, but you can’t change the collection.
Code Examples
Example 1: Using IEnumerable<T>
public IEnumerable<string> GetAllProductNames()
{
// Could return from a database query, LINQ, etc.
return products.Select(p => p.Name);
}
// Usage
foreach (var name in GetAllProductNames())
{
Console.WriteLine(name);
}
When to use:
- When you only need to iterate (loop) over the items.
- When the results might be streamed, generated, or filtered on-the-fly.
- You don’t need to know the exact number of items or their positions.
Example 2: Using IReadOnlyList<T>
public IReadOnlyList<Product> GetFeaturedProducts()
{
// Return a fixed, materialized list
return featuredProducts.AsReadOnly();
}
// Usage
for (int i = 0; i < GetFeaturedProducts().Count; i++)
{
var product = GetFeaturedProducts()[i];
Console.WriteLine($"{i}: {product.Name}");
}
When to use:
- When you need to access items by their index (e.g., list[0]).
- When you want to expose the count (list.Count).
- When you want to guarantee that the collection is fixed and can’t be changed by the consumer.
Feature | IEnumerable | IReadOnlyList |
---|---|---|
Can loop with foreach | ✅ | ✅ |
Access by index | ❌ | ✅ |
Get count | ❌ | ✅ |
Immutable/read-only | ❌ (just iterates) | ✅ (read-only contract) |
Deferred execution | ✅ (often) | ❌ (usually materialized) |
How to Decide Which to Use
-
Use
IEnumerable<T>
when you want to keep things simple, just need to loop, or want to allow streaming of results. -
Use
IReadOnlyList<T>
when you want the safety and convenience of a fixed, read-only list that exposes both count and index-based access.
Real-World Examples
Repository Pattern (Read-only)
- Use
IEnumerable<T>
for large datasets you want to stream or filter on demand. - Use
IReadOnlyList<T>
for methods that return a set of results you want to lock down (like the top 10 products).
API Response
-
Use
IReadOnlyList<T>
to return a clear, fixed collection (helps with serialization and contracts). -
Use
IEnumerable<T>
internally, especially when chaining LINQ queries.
Quick Tip for Juniors
- If you want to let others loop through your data, use
IEnumerable<T>
. - If you want to let others see how many items you have and access by index—but not modify anything—use
IReadOnlyList<T>
.
Got questions or want more real-world .NET tips? Drop a comment below!