Joshua Enfield
Joshua Enfield

Reputation: 18278

Why does the Generic List AddRange return void instead of a list?

namespace System.Collections.Generic
  public List<T>    
    public void AddRange(IEnumerable<T> collection)

It seems like it might be an intentional design decision not to return something. I got bitten by this expecting AddRange to be "fluent."

I was just curious if anyone knew of the design motivations, if there were any, to return nothing?

Upvotes: 16

Views: 6746

Answers (4)

phoog
phoog

Reputation: 43046

The other answers are (essentially) correct, but none of them addresses both possibilities for why AddRange might return something.

One reason for doing that would be to create a fluent API, like this:

list.Add(1).Add(2).Add(3);
list.AddRange(aGroupOfNumbers).AddRange(theNextGroupOfNumbers);

To achieve this, the list simply returns itself at the end of the method that mutates the list. As Trisped notes, StringBuilder was doing this before the generic List<T> was introduced.

Another reason to return something, as Felix K. implies, is that the type is immutable. If a list is immutable, it can't return itself at the end of the method, because it can't mutate itself; and it can't return void, again, because it can't mutate itself. To reflect the change defined by the method contract, the list has to create a new instance that incorporates the change, and then, of course, it has to return that new instance to the caller. People sometimes have a hard time conceiving of that when it comes to collections, but there's a very well-known type that behaves this way: System.String. As d-b says, nobody expected the list to return anything, because the "classic" imperative programming style uses void methods when mutating data structures.

The designers of the System.Collections.Generic namespace may not have thought of giving their types a fluent API, but if they did, I could see how they might have decided against it. Certainly, the case for a fluent API in StringBuilder is somewhat stronger than the case for a fluent API in List<T>.

Upvotes: 4

d--b
d--b

Reputation: 5779

The AddRange method was added in .NET 2.0, before LINQ was out, and when "fluency" really wasn't in fashion at all. At the time, no one expected AddRange to return anything. Plus, in that version of .NET, there was no initializer for List object, so you had to Add() a bunch of items to the list. The AddRange provided a shortcut for not having to loop through and Add objects one by one.

The equivalent LINQ method is IEnumerable<T>.Concat()

Upvotes: 32

Trisped
Trisped

Reputation: 6003

The AddRange method adds collection to the List<T> instance which called the function.

As a result you do not need to return anything, as the results are already there.

At the time if the work to be done did not create a new object then nothing was returned. The only exceptions which comes to mind is the StringBuilder class where most of the member methods which return a pointer to the calling instance. This was to enable command chaining and is specifically mentioned in the documentation.

Upvotes: 1

Felix K.
Felix K.

Reputation: 6281

The list is not a struct so it mutates, if you want a list which avoids mutation you have to write your own or use Linq or just use this code:

List<T> newList = new List<T>(originalList);
newList.AddRange(...);

Or use Linq when constructing the list( IEnumerable<T>.Concat() at the constructor ). There are many ways to avoid mutation.

Upvotes: 2

Related Questions