Reputation: 3
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
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