List.AddRange()
exists, but IList.AddRange()
doesn't.
This strikes me as odd. What's the reason behind this?

- 175,020
- 35
- 237
- 263

- 90,659
- 85
- 207
- 305
4 Answers
Because an interface shoud be easy to implement and not contain "everything but the kitchen". If you add AddRange
you should then add InsertRange
and RemoveRange
(for symmetry). A better question would be why there aren't extension methods for the IList<T>
interface similar to the IEnumerable<T>
interface. (extension methods for in-place Sort
, BinarySearch
, ... would be useful)

- 5,485
- 2
- 29
- 39

- 109,618
- 12
- 197
- 280
-
Also, they are very easy (I'd say, trivial) to implement yourself, which makes it a low-value feature. – ShdNx Jul 18 '12 at 10:25
-
36@ShdNx They aren't very trivial to implement performance-wise. An "internal" `AddRange/RemoveRange/InsertRange` can work directly on the "internal" collection and optimize the `Capacity` management and use methods like `Array.Copy` to move around blocks of data. An extension method `RemoveRange` would probably be an order of magniture slower than `List.RemoveRange` – xanatos Jul 18 '12 at 10:29
-
2It's too bad there wasn't (and still isn't) any way for an interface (e.g. `IFoo`) declaration to specify a "helper" namespace (e.g. `MyAssembly`) such that if a class claims to implement `IFoo` but lacks method `int Bar(String)`, the compiler would auto-generate method `int IFoo.Bar(String p1) {return MyAssembly.ClassHelpers.IFoo.Bar(this, p1);}` Had such a feature existed, interfaces could have included more methods like `AddRange` which could be implemented in terms of a base behavior, but which some implementatiosn could optimize. – supercat Nov 19 '12 at 20:06
-
1They could be implemented as extension methods, that way the interface implementation wouldn't have to implement them. Why aren't they? – Tom Pažourek Mar 05 '13 at 18:54
-
21This makes no sense. An interface abstracts an implementation, so that there can be multiple implementations of the same basic features; there's no reason why features should be omitted from an interface, because the "implementation is hard". Without methods like "AddRange" on the interface, there is no guarantee the underlying object supports them, and at that point you're forced to either implement a sub-optimal extension or defeat the purpose of using an interface by making dangerous assumptions trying to cast to a specific implementing class. Dumbed-down interfaces are over-used. – Triynko Sep 09 '15 at 19:10
-
Agreed @Triynko -- I was just burned by missing `GetRange` on this interface and the lesson I learned is to not trust that inheriting an interface actually guarantees implementation. It would make more sense to me to move these to extension methods as mentioned earlier and not undermine the integrity of the interface contract. – Dave Dec 05 '15 at 19:36
-
4There should be the IRangeList interface supporting bulk operations, implemented only on some collections which internally will have the implementation optimal. – too Jul 19 '18 at 07:51
-
You can inherit IList
and add it and use it. – ed22 May 10 '19 at 09:26 -
1I don't think that this is a particularly good answer, but it does hit at the issue. An interface does not have to be 'easy' to implement, but should cover the necessary functionality that a class should implement. Better would be, as has been mentioned in other answers, that the List
should implement further interfaces such as an additional 'ISupportsAddRangeList – Paul Williams Feb 07 '20 at 18:13', which could be implemented, as required. The 'symmetry' mentioned is all fine and good, but also hints at further interfaces, as they may not be required in all scenarios.
For those who want to have extension methods for "AddRange", "Sort", ... on IList,
Below is the AddRange
extension method:
public static void AddRange<T>(this IList<T> source, IEnumerable<T> newList)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
if (newList == null)
{
throw new ArgumentNullException(nameof(newList));
}
if (source is List<T> concreteList)
{
concreteList.AddRange(newList);
return;
}
foreach (var element in newList)
{
source.Add(element);
}
}
I created a small library that does this. I find it more practical than having to redo its extension methods on each project.
Some methods are slower than List but they do the job.
Here is the GitHub to interest them:

- 634
- 8
- 18

- 301
- 3
- 16
Since C#7 we have pattern matching which we can easily use to call the more performant List.AddRange()
method and do not need to use the as
save cast.
public static void AddRange<T>(this ICollection<T> collection, IEnumerable<T> items)
{
if (collection is null)
throw new ArgumentNullException(nameof(collection));
if (items is null)
throw new ArgumentNullException(nameof(items));
switch (collection)
{
case List<T> list:
list.AddRange(items);
break;
default:
foreach (var item in items)
{
collection.Add(item);
}
break;
}
}

- 5,148
- 3
- 31
- 51
Actually, nobody, except .Net platfom
developers and architects, can answer this question. But there are few points, which could be reasons.
In this answer, I will speak about non-generic classes, but almost all my words will be right also for generic ones.
Before I move to the explanation, I'd like to mention for those, who don't know, List<>
and all IList
implementations is not supposed to be a List in terms of common programming and data structures, that is usually mean linked list. And in the Microsoft Documentation of IList we can see definition
Represents a collection of objects that can be individually accessed by index.
So, generally, reading this definition, you mustn't have a question about "Why AddRange
is not presented in IList
", but "Why Add
is presented?". And, speaking about Add
, it is not in the IList
interface, but in the ICollection
interface. And this isa really weird thing. Why? Because almost all collections in the .Net Standard inherit ICollection
. And because of this there are many places in .Net source code, where we can see Add
implementation like in the Array class (Yes, Array
implements IList
also):
int IList.Add(Object value)
{
throw new NotSupportedException(Environment.GetResourceString("NotSupported_FixedSizeCollection"));
}
There are more things, that I can say about relations between collections interfaces in C#(also about IReadOnlyList, that was added much after IList
and looks like thing, that IList
supposed to be). But I think there is enough context, and we can start speaking about concrete reasons why IList
has no AddRange
, but List<>
has.
Not all
IList
implementations supposed to haveAddRange
method.As I mentioned above, there is a problem with
Add
method. A lot of collection in C# actually have it, but throwsNotSupportedException
, when it's called. And the same situation(even worse) would be withAddRange
method. So onlyList<>
need this method, but all other implementations ofIList
don't need it. Moreover, those developers, who decide to create their own implementation ofIList
will have to implementAddRange
, that doesn't look like thing, that is really need for simple indexed collection(which is theIList
).AddRange
is strongly dependent onList<T>
implementation.Speaking about
List<>
class. The is no non-generic class calledList
. Non-generic variant is calledArrayList
. AndArrayList
is some kind of synonym of Dynamic Array in terms of data structures. I don't know why it was decided to renameArrayList
toList
in generic collections, but I think it just increases misunderstanding about these classes in C#. So,List<T>
is a dynamic array in fact. And dynamic array would have big performance problems if you will add large count of elements to it one by one. SoAddRange
is an auxiliary and, in a sense, necessary method for dynamic array. But it is completely not necessary for indexed collection, which is theIList
.
As a conclusion, I want to say, that List<T>
and IList<T>
(just like ArrayList
and IList
), in fact, are entities, that have different semantic, and you mustn't look at them like something interchangeable. But there are some bad decisions with naming and interface relations, that have been made and led to the growing misunderstanding of relation between List<T>
and IList<T>

- 656
- 6
- 19