Search code examples
arraysvb.netclassobject

adding class object to array overwrites all array items - VB.net


I am trying to store a class object in an array. I have created the example below for my problem to make it simple.

I want to add an object of a class to an array in a loop. The object is changed at each loop pass and then added to the array. The problem is that with each loop pass the previous objects in the array are overwritten.

 Public Class Itemproperties
        Public Number As String
        Public Hight As Single
End Class
Public ItemStorage(6) As Itemproperties
Public Item As New Itemproperties

Private Sub ClassObjectToArray(sender As Object, e As EventArgs)
        For i As Integer = 0 To ItemStorage.Length - 1
            Item.Hight = i + 1
            ItemStorage(i) = Item

            Console.WriteLine(ItemStorage(0).Hight)
        Next
    End Sub

The output should be 1 1 1 1 1 1 1 1 but is 1 2 3 4 5 6 7. It seems that it is not the object that is added to the array, but rather a pointer to the object.

If I try the same system on a very simple example with an integer, it works without problems:

Dim x As Integer
Dim testarray(6) As Integer

For i As Integer = 0 To testarray.Length - 1
    x = i + 1
    testarray(i) = x
    Console.WriteLine(testarray(0))
Next

Does anyone have an idea what the problem is?

Edit:

I have tried out whether I have initialised the array or the individual elements incorrectly. If I break down the class and add it individually to the array it works, but then I have to initialise each element beforehand with testarray(i) = New Itemproperties.

Item.Hight = i + 1
ItemStorage(i) = New Itemproperties
ItemStorage(i).Hight = Item.Hight 

If I remove the breakdown but still initialise each element, unfortunately nothing changes.


Solution

  • What a great question!

    Joel on Software once noted that a class full of people having learned some programming language and then hit the part about "pointers" in the class. That's when the class drops from 70 enrolled down to less then 30!!!

    The simple way to explain this?

    When you use a variable like integer or string?

    They ARE a variable and they are referenced by "value".

    However, when you create/use an object? Your variable is ONLY a pointer to the actual object!!!

    So, say this code:

    Public Class Itemproperties
        Public Number As String
        Public Hight As Single
    End Class
    Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
    
        Dim a As New Itemproperties
    
        Dim b As Itemproperties = a
        Dim c As Itemproperties = a
    
        a.Hight = 5000
    
        Debug.Print($"b height = {b.Hight}")
        Debug.Print($"c height = {c.Hight}")
    
    
    End Sub
    

    Output:

    enter image description here

    So, a, b, and c are ALL THE SAME variable!!!!

    So, your variable is in fact a pointer to the ONE object. A nice way to think of this is when you pass values to a sub routine. Are you using By Val (a new value (copy) is passed, and thus changes in the sub don't travel back to the caller routine).

    Or, are you using By Ref (by reference, a pointer!). When you use By Ref, you are NOT passing a copy of the variable to the sub routine, but in fact are passing a reference (pointer).

    Hence a copy of the variable is not made, and any changes in the sub to that passed value will be returned.

    So, in your case?

    You are creating ONE instance of the class, and sending it over and over to the array. The result is the array is JUST holding a list of pointers to the ONE class, and each element in the array points to the ONE instance of that class. (just like b and c in the above).

    The simple solution to the above is to ensure that you create a new instance of the class each time, and then shove that into your array.

    Hence, like this:

        Dim MyProps(10) As Itemproperties
    
        For i = 1 To 10
            Dim MyItem As New Itemproperties
            MyItem.Hight = 1000 * i
            MyProps(i) = MyItem
        Next
    
        For i = 1 To 10
            Debug.Print($"height for index {i} = {MyProps(i).Hight}")
        Next
    

    Output:

    enter image description here

    So, just remember that "objects" in a variable are not in fact the object, but ONLY a pointer (reference) to the real object in memory.

    So, create a new instance each time, and you will be fine.