Boris Callens
Boris Callens

Reputation: 93327

Why doesn't IList support AddRange

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

Upvotes: 108

Views: 30502

Answers (4)

Nikolai
Nikolai

Reputation: 1165

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 does usually mean linked list. And in the Microsoft Documentation of IList we can see the 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 does not presented in IList", but "Why does Add presented?". And, speaking about Add, it is not in the IList interface, but in the ICollection interface. And this is a 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 a 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 have AddRange method.

    As I mentioned above, there is a problem with Add method. A lot of collection in C# actually have it, but throws NotSupportedException, when it's called. And the same situation(even worse) would be with AddRange method. So only List<> needs this method, but all other implementations of IList don't need it. Moreover, those developers, who decide to create their own implementation of IList will have to implement AddRange, that doesn't look like a thing, that is really needed for simple indexed collection(which the IList is).

  • AddRange is strongly dependent on List<T> implementation.

    Speaking about List<> class. The is no non-generic class called List. Non-generic variant is called ArrayList. And ArrayList is some kind of synonym of Dynamic Array in terms of data structures. I don't know why it was decided to rename ArrayList to List 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. So AddRange is an auxiliary and, in a sense, necessary method for dynamic array. But it is completely not necessary for indexed collection, which the IList is.

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>

Upvotes: 4

wertzui
wertzui

Reputation: 5720

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;
    }
}

Upvotes: 2

Emilien Mathieu
Emilien Mathieu

Reputation: 351

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:

IListExtension repository

Upvotes: 18

xanatos
xanatos

Reputation: 111860

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)

Upvotes: 74

Related Questions