List.AddRange() exists, but IList.AddRange() doesn't.
This strikes me as odd. What's the reason behind this?
Asked
Active
Viewed 2.6k times
98
Pranay Rana
- 170,430
- 35
- 234
- 261
Boris Callens
- 86,820
- 84
- 205
- 301
2 Answers
71
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)
Adrian Ratnapala
- 5,247
- 1
- 27
- 39
xanatos
- 106,283
- 12
- 188
- 265
-
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
-
19This 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.
15
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:
KevinLamb
- 624
- 10
- 17
Emilien Mathieu
- 261
- 2
- 15