1

I want to copy a collection and add new elements to only one of them (not both).

In the code example below I copy the collection by creating a new one and passing it in the constructor. After that I add an element in only one of the collections. After that both collections contains the new element...

var listOne = new Collection<Test>();

listOne.Add(new Test());
listOne.Add(new Test());

var listTwo = new Collection<Test>(listOne);

listOne.Add(new Test());

Console.WriteLine(listOne.Count); // 3
Console.WriteLine(listTwo.Count); // 3 (NOT OK)

When I use a List instead of a Collection it works as expected.

var listOne = new List<Test>();

listOne.Add(new Test());
listOne.Add(new Test());

var listTwo = new List<Test>(listOne);

listOne.Add(new Test());

Console.WriteLine(listOne.Count); // 3
Console.WriteLine(listTwo.Count); // 2 (OK)

In both cases I expect that the second list only contains 2 elements. Why is there a difference between a Collection and a List?

Liam
  • 27,717
  • 28
  • 128
  • 190
Patrick Leijser
  • 210
  • 4
  • 11
  • I expect that the collection constructor treats the collection you passed into it as a reference to the original, whereas the list one would appear to be cloning it (or just extracting the list items and copying them). – ADyson Sep 13 '19 at 10:06
  • The documentation for each of the constructors explains this, in fact: https://learn.microsoft.com/en-us/dotnet/api/system.collections.objectmodel.collection-1.-ctor?view=netframework-4.8#System_Collections_ObjectModel_Collection_1__ctor_System_Collections_Generic_IList__0__ vs https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1.-ctor?view=netframework-4.8#System_Collections_Generic_List_1__ctor_System_Collections_Generic_IEnumerable__0__ – ADyson Sep 13 '19 at 10:07
  • 1
    The answer to your question is simply "Because it was designed that way"... But I doubt anyone other than a library designer would be able to answer as to *why* they thought you'd need a wrapper for an `IList` which would be so dangerous (by which I mean, the following will throw an exception: `var x = new Collection(new int[0]); x.Add(0);`) Ew. – Matthew Watson Sep 13 '19 at 10:08

3 Answers3

2

Because Collection<T> and List<T> do different things in their constructors

Collection constructor

Initializes a new instance of the Collection class as a wrapper for the specified list.

List constructor

Initializes a new instance of the List class that contains elements copied from the specified collection [...]

Jamiec
  • 133,658
  • 13
  • 134
  • 193
2

The constructor of Collection is very simple:

public Collection(IList<T> list)
{
  if (list == null)
    ThrowHelper.ThrowArgumentNullException(ExceptionArgument.list);
  this.items = list;
}

List constructor is more complex, something like this:

public List(IEnumerable<T> collection)
{
  if (collection == null)
    ThrowHelper.ThrowArgumentNullException(ExceptionArgument.collection);
  if (count == 0)
  {
    this._items = List<T>._emptyArray;
  }
  else
  {
    this._items = List<T>._emptyArray;
    foreach (T obj in collection)
      this.Add(obj);
    this._size = count;
  }
}

This is because the Collection was created as a simple and flexible class. For this reason, it has virtual methods as opposed to List.

smolchanovsky
  • 1,775
  • 2
  • 15
  • 29
1

When you look into the source code, you see that a collection doesn't copy the given collection's content. It just references the given collection as its own items.

The list's constructor adds the given collection's items to its own items store.

pinki
  • 902
  • 2
  • 12
  • 29
  • Ok this makes sense. Still very confusing, because I did expect that when I write 'var listTwo = new Collection(listOne);' that the collection is copied... otherwise I would have written: 'var listTwo = listOne;' – Patrick Leijser Sep 13 '19 at 10:13