-1

I am trying to write a program that reads a textfile, which is neatly organized. I am trying to store the information on each line in two arrays. When I try to run the program, I get a NullReferenceException, and the program does not run. I am unsure as to what I am doing wrong.

 Private Sub RentalCostCalculator_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Dim objReader As IO.StreamReader
        Dim strFileLocation As String = "location"
        Dim strErrorMessage As String = "The file is not available. Restart when ready."
        Dim strErrorHeading As String = "Error"
        Dim intCount As Integer = 0
        Dim intFill As Integer

        ' Verifies the file exists
        If IO.File.Exists(strFileLocation) = True Then
            objReader = IO.File.OpenText(strFileLocation)

            ' Assigns the cities and city rental costs to the arrays
            Do While objReader.Peek() <> -1
                _strCityName(intCount) = objReader.ReadLine()
            
        'This is where the error occurs

                _decCityRentalCost(intCount) = Convert.ToDecimal(objReader.ReadLine())
                intCount += 1
            Loop
            objReader.Close()

            ' Displays city in listbox
            For intFill = 0 To (_strCityName.Length - 1)
                lstTopTenCities.Items.Add(_strCityName(intFill))
            Next

        Else
            MsgBox(strErrorHeading, MsgBoxStyle.Exclamation, strErrorHeading)
            Close()
        End If

    End Sub
  • Does this answer your question? [What is a NullReferenceException, and how do I fix it?](https://stackoverflow.com/questions/4660142/what-is-a-nullreferenceexception-and-how-do-i-fix-it) – David Aug 02 '21 at 20:56
  • You've got array two variables in there, _decCityRentalCost & _strCityName where are they defined and initialized? – Hursey Aug 02 '21 at 21:02
  • I didn’t show it here, they are initialized as a Public Shared variable. – EltonS. Aug 03 '21 at 22:37
  • `Dim objReader As IO.StreamReader` Declaring a variable as a type does not create an instance of the class. Use the `New` keyword to get an instance. The default value of an object type is null or `Nothing` in vb.net. Therefore the NRE. – Mary Aug 04 '21 at 02:36

1 Answers1

2

Paired arrays that match up by index are an anti-pattern: something to avoid. Better to make a single collection with a custom Class type or tuple.


This is less intuitive, but it's also poor practice to check if the file exists ahead of time. The better pattern handles the exception in the case where the file access fails. The file system is volatile, meaning you still have to be able to handle exceptions on file access, even if an Exists() check passes. We have to write this code anyway, so we may as well rely on it as the main file exists check, too.

But isn't handling exceptions slow? I'm glad you asked. Yes, yes it is. In fact, unrolling the stack to handle an exception is up there among the slowest things you can do in a single computer, which is why exceptions are normally avoided for program flow control like this. But you know what's even worse? Disk I/O. Disk I/O is so much worse even than exception handling. In checking if the file exists up front, we pay for an extra tip out to disk every time, where with exceptions we only the pay the performance penalty if the file access fails.

In summary: write more code and pay a worse cost every time, or pay a still-bad-but-less-cost some of the time, with less code. Skipping the File.Exists() check should be a no-brainer.

Finally, I don't know who taught you to use prefixes like str and obj with your variables, but that's no longer good advice. It made sense back in the vb6 era, but since since then we have a better type system and better tooling, and with the release of VB.Net way back in 2002 Microsoft (who invented the practice) updated their own style guidelines to say not to use those prefixes. It is still common practice to use prefixes for the WinForms control types, but otherwise it's best to avoid them.

Here's a solution that incorporates each of those points, and very likely solves the NullReferenceException as well.

Private Iterator Function ReadRentalFile(filePath As String) As IEnumerable(Of (String, Decimal))
    Using rdr As New IO.StreamReader(filePath)
          Dim City As String = Nothing
          While (City = rdr.ReadLine()) IsNot Nothing
                Yield (City, Decimal.Parse(rdr.ReadLine()))
          End While
    End Using
End Function

Private _cityCosts As IEnumerable(Of (String, Decimal))

Private Sub RentalCostCalculator_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    Dim FileLocation As String = "location"
    Try
        _cityCosts = ReadRentalFile(FileLocation)
        lstTopTenCities.Items.AddRange(_cityCosts.Select(Function(c) c.Item1).ToArray())
    Catch
        MsgBox("Error", MsgBoxStyle.Exclamation, "The file is not available. Restart when ready.")
    End Try
End Sub

But looking at the original code, if the error occurs on this line:

_decCityRentalCost(intCount) = Convert.ToDecimal(objReader.ReadLine())

It's very likely that either the file is not quite as neatly-organized as you expect, and there's no result from objReader.ReadLine(), or (even more likely) that _decCityRentalCost doesn't refer to an actual array at this point, where it was never actually instantiated or the variable was changed to point somewhere else. Given this is in a Form_Load method, it's probably the former.

Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
  • Hello @JoelCoehoorn , thank you for your detailed answer, I appreciate it a lot. I don’t think I’m advanced enough to be able to understand the solution code you proposed. I learned from a book from 2016 , as per my professor. – EltonS. Aug 03 '21 at 22:39