C#中的StreamReader在try/finally块中

12

今天我有一个问题涉及到StreamReader类。具体来说是使用文件名参数初始化该类,例如:

TextReader tr = new StreamReader(fileName);

显然,当此操作完成后,关闭流是很重要的:

 tr.Close();

我希望将这段代码放入 try / finally 块中,但是我找不到方法。以下是一些我发现行不通的变化:

    try
        {
            var serializer = new XmlSerializer(type);
            TextReader tr = new StreamReader(fileName);
            var obj = serializer.Deserialize(tr);
        }
    finally
        {
            tr.Close();    
        }

而且更糟糕的是:

     TextReader tr;  
        try
        {
            var serializer = new XmlSerializer(type);
            tr = new StreamReader(fileName);
            var obj = serializer.Deserialize(tr);
        }
        finally
        {
            tr.Close();    
        }

在 finally 块中关闭 StreamReader,这样做是否可行?


声明 TextReader tr; 以便在 finally 中使用并不是问题。但是使用 using 更好。 - paparazzo
5个回答

27

最简单的方法是使用 using 语句:

using (TextReader tr = new StreamReader(fileName))
{
  // ...
}
编译器会为您生成try-finally块,并在finally中放置调用Close(实际上是Dispose)的代码。
如果您需要明确指出finally,您的第二个示例将起作用,除了您需要强制初始化tr:
TextReader tr = null;

当然,在finally块中你需要检查tr != null,以防在执行tr = new StreamReader(...)之前发生异常。


真遗憾,你比我先发了帖子 :) - Joel Etherton
1
那就意味着close方法不需要手动调用了吗? - JL.
1
JL:正确。使用using将确保为您调用Dispose方法,并且对于TextReader,Close和Dispose是相同的(出于历史原因,保留了Close名称)。如果您想出于某种原因提前关闭读取器,则可以在using块内手动调用Close,但编译器将作为其扩展using关键字的一部分生成Dispose调用。因此,如果使用using,不需要手动调用Close。 - itowlson

8

是的,两者都可以:

TextReader tr = null;
try
{
    var serializer = new XmlSerializer(type);
    tr = new StreamReader(fileName);
    var obj = serializer.Deserialize(tr);
}
finally
{
    if (tr != null)
        tr.Close();    
}

或者只是:
using (TextReader tr = new StreamReader(fileName))
{
    var serializer = new XmlSerializer(type);
    var obj = serializer.Deserialize(tr);
}

你的问题中第一段代码无法编译的原因是 tr 变量被声明在 try 块内,这使它无法被 finally 块访问。
你的问题中第二段代码无法编译的原因是 tr 变量没有被赋值,如果 new XmlSerializer 抛出异常,try 块也不会得到任何异常信息,这意味着当变量到达 finally 块时可能有一个未定义的值。
解决方案,如果你想绝对保持 try/finally 结构,就要确保变量有一个初始值,并且只有在它改变时才调用 .Close
当然,最正确的方法是使用 using 块,它为你处理了所有细节。请注意,这将更改读取器和序列化器对象之间的构造顺序,或者你可以像这样编写代码,保留你在问题中的顺序(在这种情况下不会有影响):
var serializer = new XmlSerializer(type);
using (TextReader tr = new StreamReader(fileName))
{
    var obj = serializer.Deserialize(tr);
}

3

关于这段代码的注释

TextReader tr;
        try
        {
            var serializer = new XmlSerializer(type);
            tr = new StreamReader(fileName);
            var obj = serializer.Deserialize(tr);
        }
        finally
        {
            //tr.Close(); 
            // 正确的方式
            if (tr != null) tr.Close();
        }

如果StreamReader失败,那么finally块将被执行,在这里,由于失败的结果,tr仍然为null,因此你将在finally块内部遇到异常!

最好捕获System.IO.IOException,以便正确处理文件输入/输出处理问题,而不是掩盖它...


2

不要在此处使用try/finally,而应使用Using语句,它具有与您使用的代码相同的功能,但语法更简洁。顺便说一下,答案是“是” :)


2
你应该使用using关键字。
using (TextReader tr = new StreamReader(fileName))
{
    var obj = serializer.Deserialize(tr);
}

当对象在块的末尾超出范围时,将调用Dispose()方法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接