Why doesn't IList support AddRange
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
IList<T> does not have an AddRange method because interfaces in .NET are designed to define the minimum contract that all implementations must support. AddRange is a convenience method on List<T> (the concrete class) that calls Add in a loop with internal optimizations. Including it in the interface would force every implementer — arrays, read-only collections, custom lists — to provide a bulk-add operation, which does not make sense for all collection types.
The Interface vs Concrete Class
AddRange is an optimization on List<T> that pre-allocates capacity and copies elements in bulk. Requiring this on the interface would be burdensome for simple implementations.
Workaround 1: Extension Method
Create an extension method that works on any IList<T>:
This approach checks if the underlying type is List<T> and uses its optimized AddRange when possible, falling back to individual Add calls otherwise.
Workaround 2: Cast to List
If you know the underlying type is List<T>:
Workaround 3: Use List in Method Signatures
If you control the API, accept List<T> instead of IList<T> when you need AddRange:
Why .NET Interfaces Are Minimal
.NET follows the Interface Segregation Principle — interfaces should not force implementers to provide methods they do not need:
Adding AddRange to IList<T> would mean more implementations throwing NotSupportedException, which defeats the purpose of having a contract.
ICollection vs IList vs List
LINQ Alternatives
Instead of AddRange, LINQ provides ways to combine collections:
Note that Concat and Union create new collections rather than modifying the original.
Common Pitfalls
- Assuming
IList<T>supports allList<T>methods:IList<T>only defines indexed access and individual add/remove. Methods likeAddRange,Sort,Find, andBinarySearchare onList<T>only. Check the interface definition before calling methods. - Catching
NotSupportedExceptionas normal flow: Arrays and read-only collections implementIList<T>but throw on mutation methods. Instead of catching exceptions, checkICollection<T>.IsReadOnlybefore callingAdd. - Overusing
List<T>in APIs: ExposingList<T>in public API signatures locks callers into that implementation. UseIList<T>orIReadOnlyList<T>for parameters and return types, and only useList<T>internally when you need its specific methods. - Extension method performance: An extension method that calls
Addin a loop does not pre-allocate capacity. For large collections, this causes repeated array resizing. The workaround of casting toList<T>when possible avoids this overhead. - Forgetting
IReadOnlyList<T>: When you only need to read from a list, useIReadOnlyList<T>instead ofIList<T>. It communicates intent clearly and prevents accidental mutation.
Summary
IList<T>omitsAddRangebecause interfaces define minimal contracts, not convenience methodsAddRangeis aList<T>-specific optimization that pre-allocates and bulk-copies- Use an extension method to add
AddRangetoIList<T>, with a fast path forList<T> - Choose the narrowest interface type:
IEnumerable<T>for reading,IList<T>for mutation,List<T>for bulk operations - Check
IsReadOnlybefore calling mutation methods onIList<T>— arrays and read-only wrappers throw onAdd - Prefer
IReadOnlyList<T>when the consumer does not need to modify the collection

