使用 StreamReader 的 Null 异常

Null Exception using StreamReader

提问人:EltonS. 提问时间:8/3/2021 更新时间:2/16/2022 访问量:223

问:

我正在尝试编写一个读取文本文件的程序,该程序组织整齐。我正在尝试将每行的信息存储在两个数组中。当我尝试运行程序时,我得到一个 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
vb.net nullreferenceexception

评论

0赞 David 8/3/2021
这回答了你的问题吗?什么是 NullReferenceException,如何修复它?
0赞 Hursey 8/3/2021
你有数组两个变量,_decCityRentalCost和_strCityName它们在哪里定义和初始化?
0赞 EltonS. 8/4/2021
我没有在这里展示它,它们被初始化为公共共享变量。
0赞 Mary 8/4/2021
Dim objReader As IO.StreamReader将变量声明为类型不会创建类的实例。使用关键字获取实例。对象类型的默认值为 null 或 in vb.net。因此,NRE.NewNothing

答:

2赞 Joel Coehoorn 8/3/2021 #1

按索引匹配的成对数组是一种反模式:这是要避免的。最好使用自定义类类型或元组创建单个集合。


这不太直观,但提前检查文件是否存在也是一种糟糕的做法。更好的模式在文件访问失败的情况下处理异常。文件系统是易失性的,这意味着即使检查通过,您仍然必须能够处理文件访问的异常。无论如何,我们都必须编写此代码,因此我们也可以依赖它,因为主文件也存在检查。Exists()

但是,处理异常不是很慢吗?我很高兴你问。是的,是的。事实上,展开堆栈以处理异常是您在单台计算机中可以执行的最慢的事情之一,这就是为什么通常避免此类程序流控制异常的原因。但你知道还有什么比这更糟糕的吗?磁盘 I/O。磁盘 I/O 甚至比异常处理还要糟糕得多。在预先检查文件是否存在时,我们每次都会支付额外的磁盘提示,除了例外情况,我们只在文件访问失败时支付性能损失。

总而言之:编写更多的代码,每次都付出更糟糕的代价,或者在某些时候付出更糟糕但更低的代价,用更少的代码。跳过检查应该是不费吹灰之力的。File.Exists()

最后,我不知道是谁教你使用像 和 这样的前缀与你的变量一起使用,但这不再是好的建议。这在 vb6 时代是有道理的,但从那时起,我们有了更好的类型系统和更好的工具,并且随着 2002 年 VB.Net 的发布,Microsoft(发明了这种做法)更新了他们自己的风格指南,说不要使用这些前缀。对 WinForms 控件类型使用前缀仍然是常见的做法,但除此之外,最好避免使用前缀。strobj

下面是一个解决方案,它包含了这些点中的每一个,并且很可能还解决了 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()_decCityRentalCostForm_Load

评论

0赞 EltonS. 8/4/2021
您好@JoelCoehoorn,感谢您的详细回答,非常感谢。我不认为我足够先进,无法理解您提出的解决方案代码。根据我的教授的说法,我从 2016 年的一本书中学到了东西。