What does it mean by variable has been used before it has been assigned a value?

I created this program with the intention of bubble sorting "numbers.txt" textfile. But the line below that is bolded is giving me an error of variable has been used before it has been assigned a value. Can someone help me at this? I seem to cant figure out this line numbers2(z) = numbers(z). Cause every time I load this program, it crashes on me. I would love someone to give me some assistance

 Public Class Form1
    Dim currentLine As String = ""
    Dim data As New System.IO.StreamReader("numbers.txt")
    Dim counter As Integer = 0
    Dim currentValue As Integer
    Dim previousValue As Integer
    Dim nextValue As Integer
    Dim isLoaded As Boolean = False
    Dim numbers As String()
    Dim numbers2 As String()
    Public Sub btnSort_Click(sender As Object, e As EventArgs) Handles btnSort.Click
        If (Not isLoaded) Then
            MessageBox.Show("You have not loaded")
        Else

            For j = 0 To counter
                If (j = 0) Then
                    currentValue = CInt(numbers2(j))
                    nextValue = CInt(numbers2(j + 1))
                    If (currentValue > nextValue) Then
                        numbers2(j + 1) = numbers2(j)
                        numbers2(j) = numbers(j + 1)
                    End If
                ElseIf (j = counter) Then
                    Continue For
                ElseIf (j = counter - 1) Then
                    currentValue = CInt(numbers2(j))
                    previousValue = CInt(numbers2(j - 1))
                    If (currentValue > previousValue) Then
                        'Good
                    ElseIf (currentValue < previousValue) Then
                        numbers2(j - 1) = numbers2(j)
                        numbers2(j) = numbers(j - 1)

                    End If
                Else
                    currentValue = CInt(numbers2(j))
                    previousValue = CInt(numbers2(j - 1))
                    nextValue = CInt(numbers(j + 1))
                    If (currentValue < nextValue) Then
                        'Good
                    ElseIf (currentValue > nextValue) Then
                        numbers2(j + 1) = numbers2(j)
                        numbers2(j) = numbers(j + 1)
                    End If
                    If (currentValue > previousValue) Then
                        'Good
                    ElseIf (currentValue < previousValue) Then
                        numbers2(j - 1) = numbers2(j)
                        numbers2(j) = numbers(j - 1)

                    End If

                End If
            Next

            For k = 0 To counter
                tbOutput.Text += numbers2(k) & vbCrLf
            Next
            For z = 0 To counter
                numbers(z) = numbers2(z)
            Next
        End If
    End Sub

    Public Sub btnLoad_Click(sender As Object, e As EventArgs) Handles btnLoad.Click
        Dim numbers As String() = currentLine.Split(New String() {Environment.NewLine}, StringSplitOptions.None)
        Dim numbers2 As String()

        Do While data.Peek() <> -1
            'Appends currentline with results from data.readline and a new line
            currentLine = currentLine & data.ReadLine() & vbNewLine
            'shows the amount of lines in the file
            counter += 1

        Loop
        'displays content from the file
        tbInput.Text = currentLine
        'sets 

        For z = 0 To counter
            **numbers2(z) = numbers(z)**
        Next

        isLoaded = True
    End Sub

    Private Sub tbOutput_TextChanged(sender As Object, e As EventArgs) Handles tbOutput.TextChanged

    End Sub
End Class

Upvotes: 0

Views: 681

Answers (1)

Joel Coehoorn
Joel Coehoorn

Reputation: 416121

Fundamentally, this question speaks to understanding of the differences between variables, object references, object instances, and types. Until you really understand those differences, you will continue to struggle as a coder. Don't feel bad; you're far from alone here, and once you do get this suddenly everything is a whole lot easier.

numbers2 is a variable. Like all variables in .Net, numbers2 has a specific type: in this case, string array, which is a reference type. But that is not the same thing as saying numbers2 is itself an array. An actual array is an instance of an Object, and like all object instances exists off in memory somewhere separate from any variable. You must then give the numbers2 variable a reference to the array object. References connect variables with objects.

The reference is not the object, because there may be many references all pointing to the same object. But without a reference, an object is useless and the memory will be reclaimed by the garbage collector. The reference is not the variable, because one variable may have several different references assigned to it over its lifetime. But for the span of time the reference is assigned, the value of the variable is that reference. And a variable most certainly is not (by itself) an object. Even the Form1 variable is simply an object reference variable, and you could assign a whole new Form1 object instance to it (that VB.Net allows you to have variables and types with the same name can be frustrating, and I believe doing this by default with Windows forms is the source of much confusion in this whole "types vs reference vs object" area for many new programmers).

One way to assign a reference is while creating a new object. This is what happens when you see a Dim statement with the New keyword on the same line. This is also what happens when you have a Dim statement for array that includes a subscript (size), such as Dim items(4) As String or Dim items As String(4). The compiler here will automatically create the array object. But if you don't include a size for an array (Dim numbers2() As String), the compiler does not yet have enough information to create the array, because it doesn't know how big the object needs to be. So now you have an object reference variable, but there's no object instance assigned to it yet.


Now we know enough to begin debugging this program.

We'll start in the Load method. The beginning of this method re-declares the numbers and numbers2 arrays:

Dim numbers As String() = currentLine.Split(New String() {Environment.NewLine}, StringSplitOptions.None)
Dim numbers2 As String()

This means the method is working with completely different variables from the arrays defined at the top of the class, and so later on, in the Sort method, there's no data; the arrays are still null/Nothing.

Additionally, the declaration for numbers2 doesn't assign anything. It just creates an empty array reference. This is different than having an array object with 0 elements. There is no array object here at all yet. This is what causes the exception on the line indicated. You are trying to use a numbers2 reference variable before any object has been assigned to it.


To fix things, what I would do first of all is separate the event handlers from the code that does work; limit event code as much as possible to the statements the deal with reading and writing control properties. We'll make a new LoadData() function which accepts an argument and returns a value. The new method is not concerned with any textboxes, global or classs variables, or controls. It merely knows how to read the data in the format most appropriate for the data:

Public Function LoadData(fileName As String) As IEnumerable(Of Integer)
     Return File.ReadLines(fileName).Select(Function(line) Integer.Parse(line.Trim()))
End Function

Now the existing Load method can call this function. Even better if this method handles the Form_Load, Form_Shown, Form_Activated, or similar event depending on what is most appropriate for your application, rather than a button click. Also, let's choose a better type for our numbers object:

Private numbers As List(Of Integer)

Public Sub Form1_Load(sender As Object, e As EventArgs) Handles Form1.Load
    numbers = LoadData("numbers.txt").ToList()

    tbInput = String.Join("\n", numbers)
End Sub

We could make this more efficient by removing a conversion back to string, but that version requires a lot more code, and it's won't make a meaningful difference until you have very large collections.

Now let's look at the sort method:

 Public Sub btnSort_Click(sender As Object, e As EventArgs) Handles btnSort.Click
    If numbers Is Nothing OrElse numbers.Count = 0 Then
        MessageBox.Show("You have not loaded")
        Return
    End If

    numbers = numbers.OrderBy(Function(n) n).ToList()
    tbOutput.Text = String.Join("\n", numbers)
End Sub

Put it all together:

Public Class Form1

    Private numbers As List(Of Integer)

    Public Sub btnSort_Click(sender As Object, e As EventArgs) Handles btnSort.Click
        If numbers Is Nothing OrElse numbers.Count = 0 Then
            MessageBox.Show("You have not loaded")
            Return
        End If

        numbers = numbers.OrderBy(Function(n) n).ToList()
        tbOutput.Text = String.Join("\n", numbers)
    End Sub

    Public Sub Form1_Load(sender As Object, e As EventArgs) Handles Form1.Load
        numbers = LoadData("numbers.txt").ToList()

        tbInput = String.Join("\n", numbers)
    End Sub

    Public Function LoadData(fileName As String) As IEnumerable(Of Integer)
         Return File.ReadLines(fileName).Select(Function(line) Integer.Parse(line.Trim()))
    End Function
End Class

Now I suspect this is course work, where your instructor does not want you to use OrderBy() or Array.Sort(). But even in that case, follow the example from the LoadData() function: create a method separate from the button event handler which accepts an IEnumerable(Of Integer) and returns a sorted IOrderedEnumerable(Of Integer), and use the button click event handler to call this method. The result will be code that retains this same basic structure, where you still only need the one class-level variable.

Upvotes: 3

Related Questions