为什么XmlReader没有关闭FileStream

10

所以我正在使用XmlReader内部的FileStream

using (XmlReader reader = XmlReader.Create(new FileStream(archivePath, FileMode.Open), readerSettings))
{
    reader.close()
}

然而,将文件馈送至XmlReader后,在using作用域之后该文件仍处于锁定状态,奇怪的是,我以为XmlReader会替我关闭FileStream,它没有吗?

谢谢帮忙。

4个回答

15

您可以通过XmlReaderSettings.CloseInput来控制这个。

readerSettings.CloseInput = true;
using (XmlReader reader = XmlReader.Create(new FileStream(archivePath, FileMode.Open), readerSettings))
{
    // do work with the reader
}

或者更简洁点,如果您不关心其他读者的设置:

using (XmlReader reader = XmlReader.Create(new FileStream(archivePath, FileMode.Open), new XmlReaderSettings() { CloseInput = true }))
{
    // do work with the reader
}

1
好的答案,如果你将CloseInput=false,那么它会阻止FxCop对多个处置的抱怨,这一点让我很烦恼。 - satnhak
这里的reader.close()语句是否多余?因为退出using语句将调用Dispose(),并且Dispose()会调用Close()。 - Kent

11

你尝试过这个吗?

using(var stream = new FileStream(archivePath, FileMode.Open))
using(var reader = XmlReader.Create(stream, readerSettings))
{

}

我在文档中没有找到任何明确说明当XmlReader被处理时是否会调用基础流的dispose。另外,我总是像上面展示的那样使用它,从未遇到问题。

通过反射浏览器,我也没有发现在创建XmlTextReaderImpl时调用Dispose()的实例。 XmlTextReaderImpl不实现Dispose()方法,其Close()方法看起来像这样:

internal void Close(bool closeInput)
{
    if (this.parsingFunction != ParsingFunction.ReaderClosed)
    {
        while (this.InEntity)
        {
            this.PopParsingState();
        }
        this.ps.Close(closeInput);
        this.curNode = NodeData.None;
        this.parsingFunction = ParsingFunction.ReaderClosed;
        this.reportedEncoding = null;
        this.reportedBaseUri = string.Empty;
        this.readState = ReadState.Closed;
        this.fullAttrCleanup = false;
        this.ResetAttributes();
    }
}

1
说实话,不是的。我以为XmlReader和StreamReader一样,完成后会关闭内部流。使用两个using语句有点笨拙。 - TOMMY WANG
1
虽然这只是个案例,但我看到的大多数例子都使用了两个“using”。 - Grant H.
2
@TOMMYWANG:你可能这样认为,但是我在代码中(使用反射器)找不到任何地方XmlTextReaderImpl(实际上是XmlReader.Create()返回的内容)处置流。如果它关闭了流,您将在使用一个流用于多个读取器时遇到问题(如果我想保持流的活动状态怎么办?为什么读取器要决定我不能这样做?),因此采取安全方法什么都不做。我不认为它很“笨拙”,但“笨拙”比“损坏”好。 - Ed S.
@TOMMYWANG:没问题,很高兴能帮忙 :) - Ed S.
我宁愿底层流不被读取器关闭,因为您可能仍然想在以后使用该流,但是由于已经关闭,所以无法使用!因此,这是我认为最好的行为。 - Josh M.

1

你需要跟踪FileStreamXmlReader。让XmlReader关闭底层流是潜在的危险。如果FileStream被多个读取器使用:如果其中一个读取器关闭了流,这将导致其他读取器意外失败。

这有点麻烦,因为一些流读取器和写入器会关闭底层流,而另一些则不会。作为最佳实践,我总是手动关闭和处理我打开的流。这也有助于减轻某些流的“坑点”。
例如:在调用.ToArray()之前,您需要处理GZipStream


0
或许这篇文章已经晚了几年,但或许它能帮到某些人...
我尝试了Eric的方法,因为它看起来是一个不错的解决方案,但是当我在VS代码分析中运行它时,我一直收到警告CA2202
CA2202的底部附近,Microsoft建议使用以下内容:
(我稍微修改了它以适用于“XmlReader”。)
Stream stream = null;
try
{
    stream = new FileStream("file.txt", FileMode.Open);
    using (XmlReader reader = new XmlReader (stream))
    {
        stream = null;
        // Use the reader object...
    }
}
finally
{
    if(stream != null)
        stream.Dispose();
}

而不是...

using (Stream stream = new FileStream("file.txt", FileMode.Open))
{
    using (XmlReader reader = new XmlReader (stream))
    {
        // Use the reader object...
    }
}

虽然更长,但至少不会抛出任何警告。


这将导致相同的不正确行为,即实际上根本没有处理流,因为 XmlReader 默认情况下不会处理其流,原因不明。好消息是,这个特定的警告似乎不再发生了,所以看起来他们修复了这个错误的提示。 - Miral

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