0

The System.Collections.Generic as most of C# developers knows, contains the List class. The class exposes the Add method, which is implemented as follows:

[__DynamicallyInvokable]
public void Add(T item)
{
    if (this._size == this._items.Length)
    {
        this.EnsureCapacity(this._size + 1);
    }
    T[] arg_36_0 = this._items;
    int size = this._size;
    this._size = size + 1;
    arg_36_0[size] = item;
    this._version++;
}

I am trying to understand how this works... I have a local array of type T, called "_items", there are also the "_size" and "_version" variables. When a new item is added, a new array of type T is created and called "arg_36_0", the private array is assigned to the new one. size is copied; then the local size is incremented, and to the newly created array we are assigning the passed new item. So after this function finishes, the local "arg_36_0" array will be removed, the item is never assigned to the actual array (called "_items"). What am I missing here? :)

Eru
  • 332
  • 3
  • 17
  • 1
    not the array is removed, but just a reference to it. You have only a single array, not two. Whatever you´re doing on one of the references is reflected via the other one also. – MakePeaceGreatAgain Aug 19 '20 at 16:23
  • 1
    The important thing to realize is that an array is a reference type and not a value type so the `arg_36_0` is a variable that references the same array as `_item` does so when you add a value to it you are actually adding a value to the same array referenced by `_item`. – juharr Aug 19 '20 at 16:25
  • What is this line of code doing? arg_36_0[size] = item; – jdweng Aug 19 '20 at 16:27
  • 1
    @jdweng it is adding the new `item` at index `size`, which is the new (increased) size. Not to mix with the capacity of the array, though. – MakePeaceGreatAgain Aug 19 '20 at 16:31
  • The OP said "the item is never assigned to the actual array" which is not true. – jdweng Aug 19 '20 at 16:36
  • The implementation of Add in .NET 4.8 is more straight forward, or easier to understand by reading it. In the end it produces the same result. I'm assuming you are showing the .NET Core version? – insane_developer Aug 19 '20 at 16:51
  • where did you get this implementation of the `List.Add` ? [This](https://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs,9cc11588bffd57c1,references) is what the source code actually have – Franck Aug 19 '20 at 17:22
  • You might have an easier time reading the actual source code https://github.com/microsoft/referencesource/blob/master/mscorlib/system/collections/generic/list.cs instead of decompiling. – Jason Aug 19 '20 at 17:24
  • I wonder what is the need to create another variable `arg_36_0`, why not just work directly on `this._items`? – srh Aug 19 '20 at 17:27
  • 1
    @srh when you look into the **actual** code you see that it uses only a single reference. The above code is just the decompiled code, not neccessarily optimized for readability. – MakePeaceGreatAgain Aug 19 '20 at 17:46

1 Answers1

1

You´re missing reference-type semantics.

T[] arg_36_0 = this._items

will just create a new reference to the exact same array.

The point of reference-types is that whatever you´re doing on one of those references is reflected on all references.

So arg_36_0[size] = item; will change the underlying array. You could also write this:

int size = this._size;
this._size = size + 1;
this._items[size] = item; // set the last element in the array
this._version++;

Imagine you´d have a class like this:

class MyClass
{
    int MyProperty { get; set; }
}

and create an instance of it:

var a = new MyClass();

If you would now introduce a second reference to that object you would see changes on both a and b:

var b = a;
b.MyProperty = 3; // this changes the underlying object and thus is reflected in a also
Console.WriteLine(a.MyProperty); // prints 3
MakePeaceGreatAgain
  • 35,491
  • 6
  • 60
  • 111