提问人:EltonS. 提问时间:8/3/2021 更新时间:2/16/2022 访问量:223
使用 StreamReader 的 Null 异常
Null Exception using StreamReader
问:
我正在尝试编写一个读取文本文件的程序,该程序组织整齐。我正在尝试将每行的信息存储在两个数组中。当我尝试运行程序时,我得到一个 NullReferenceException,并且程序没有运行。我不确定我做错了什么。
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
答:
按索引匹配的成对数组是一种反模式:这是要避免的。最好使用自定义类类型或元组创建单个集合。
这不太直观,但提前检查文件是否存在也是一种糟糕的做法。更好的模式在文件访问失败的情况下处理异常。文件系统是易失性的,这意味着即使检查通过,您仍然必须能够处理文件访问的异常。无论如何,我们都必须编写此代码,因此我们也可以依赖它,因为主文件也存在检查。Exists()
但是,处理异常不是很慢吗?我很高兴你问。是的,是的。事实上,展开堆栈以处理异常是您在单台计算机中可以执行的最慢的事情之一,这就是为什么通常避免此类程序流控制异常的原因。但你知道还有什么比这更糟糕的吗?磁盘 I/O。磁盘 I/O 甚至比异常处理还要糟糕得多。在预先检查文件是否存在时,我们每次都会支付额外的磁盘提示,除了例外情况,我们只在文件访问失败时支付性能损失。
总而言之:编写更多的代码,每次都付出更糟糕的代价,或者在某些时候付出更糟糕但更低的代价,用更少的代码。跳过检查应该是不费吹灰之力的。File.Exists()
最后,我不知道是谁教你使用像 和 这样的前缀与你的变量一起使用,但这不再是好的建议。这在 vb6 时代是有道理的,但从那时起,我们有了更好的类型系统和更好的工具,并且随着 2002 年 VB.Net 的发布,Microsoft(发明了这种做法)更新了他们自己的风格指南,说不要使用这些前缀。对 WinForms 控件类型使用前缀仍然是常见的做法,但除此之外,最好避免使用前缀。str
obj
下面是一个解决方案,它包含了这些点中的每一个,并且很可能还解决了 NullReferenceException。
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
但是看原始代码,如果错误发生在这一行:
_decCityRentalCost(intCount) = Convert.ToDecimal(objReader.ReadLine())
很有可能文件没有你预期的那么整齐,并且没有来自的结果,或者(甚至更有可能)在这一点上没有引用实际的数组,它从未实际实例化或变量被更改为指向其他地方。鉴于这是在一种方法中,它可能是前者。objReader.ReadLine()
_decCityRentalCost
Form_Load
评论
Dim objReader As IO.StreamReader
将变量声明为类型不会创建类的实例。使用关键字获取实例。对象类型的默认值为 null 或 in vb.net。因此,NRE.New
Nothing