如何获得 File.OpenText 到 return 不可为空的 StreamReader

How to get File.OpenText to return non-nullable StreamReader

考虑以下因素:

var lines = new List<string>();
string path = "FileName.txt";
var reader = File.OpenText(path);
while (!reader.EndOfStream)
{
    lines.Add(reader.ReadLine());
}

Nullable 标志设置为 enable 时,在 csproj 中,reader 是一个 StreamReader?,会给出 Possible null reference argument for parameter 'item' 编译器警告。即使我明确地将 reader 设置为 StreamReader,问题仍然存在。

有没有办法在不关闭 Nullable 的情况下绕过这个警告?

我认为,问题描述中包含的警告并不完整。我重现了这个问题并得到了下一个编译器警告:CS8604 Possible null reference argument for parameter 'item' in 'void List<string>.Add(string item)。警告出现在下一行

lines.Add(reader.ReadLine());

因为 List<string> lines 是不可为空的 string 值的列表,并且方法 StreamReader.ReadLine() 可以 return 为空 string? (参见 reference source).因此,在上面的代码行中,这些是尝试将 null 字符串值添加到不可为 null 的字符串列表中。

要解决此问题,您可以使用以下任一方法:

  • Use null-forgiving operator. 你肯定知道表达式的结果 reader.ReadLine() 不能是 null 因为它是在未到达流末尾时执行的。因此,您可以使用 null-forgiving 运算符来抑制警告:

    lines.Add(reader.ReadLine()!);
    
  • 检查表达式reader.ReadLine()的结果是否为空。如果使用下一个警告将消失的代码:

    string? line = reader.ReadLine();
    if (line != null)
        lines.Add(line);
    

更新

正如@IanRingrose 在评论中指出的那样,可以在循环中使用条件 line != null 而不是条件 !reader.EndOfStream()。使用这样的条件解可以更清楚:

string? line;

while ((line = reader.ReadLine()) != null)
{
    lines.Add(line);
}

现在我们不需要在循环体中使用 null-forgiving 运算符或附加条件。

回答您关于 reader 本身的可空性的具体问题:reader 是一个 StreamReader?,因为 NRT 决定使用 var 将推断变量的类型为 nullable.

看到这个section of the spec:

nullable implicitly typed local variables

var infers an annotated type for reference types. For instance, in var s = ""; the var is inferred as string?.

language design notes 解释这个决定。最终他们得出结论:

Make var have a nullable annotated type and infer the flow type as normal.

想法是,由于没有 var? 的语法,该类型被推断为最不严格的类型(即,允许稍后对变量进行 null 赋值)。最终,可空性 应该 通过流分析进行跟踪。

也就是说,已知使用 OpenText 总是 return 一个非 null 对象(否则它会抛出)并且有一个 non-nullable return type。要解决您的问题,只需将您的变量直接声明为 StreamReader:

StreamReader reader = File.OpenText(path);

正如其他答案所指出的,我认为您对实际错误及其发生位置感到困惑。 “项目”唯一会出现的地方是调用 List<>.Add 时。当然,这是因为 StreamReader.ReadLine 可以 return null 而您的列表声明为 List<string> 而不是 List<string?>

您的选择是:

  • 更改列表的类型(可能不会)
  • 使用 null-forgiving 运算符 reader.ReadLine()!(也许,但只有在保证的情况下)
  • 使用局部变量并在添加到列表之前添加对 null 的检查(直接或通过修改循环条件来执行赋值和空检查)

您可以将所有代码减少到只有两行 Nullable-friendly 行:

string path = "FileName.txt";
var lines = File.ReadLines(path).ToList();