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.