在 XmlWriter 周围使用语句会导致过早关闭流

Using statement around XmlWriter causes premature closing of stream

我正在尝试将一小段XML写入内存流,然后从内存流中读取写入的内容并保存到变量中。

我有以下适用于写入和读取简单文本值的逻辑:

string text = null;

using (var memoryStream = new MemoryStream())
{
    using (var streamWriter = new StreamWriter(memoryStream))
    {
        streamWriter.Write("Foo!");
        streamWriter.Flush();

        memoryStream.Position = 0;

        using (var streamReader = new StreamReader(memoryStream))
        {
            text = streamReader.ReadToEnd();
        }
    }
}

await context.Response.WriteAsync(text, Encoding.UTF8);

但是用 XmlWriter 替换 StreamWriter 并尝试写入实际的 XML 结果是 ObjectDisposedException.

string text = null;

using (var memoryStream = new MemoryStream())
{
    using (var xmlWriter = XmlWriter.Create(memoryStream))
    {
        xmlWriter.WriteStartDocument();
        xmlWriter.WriteStartElement("Foo");
        xmlWriter.WriteEndElement();
        xmlWriter.WriteEndDocument();
        xmlWriter.Flush();

        memoryStream.Position = 0;

        using (var streamReader = new StreamReader(memoryStream))
        {
            text = streamReader.ReadToEnd();
        }
    }
}
System.ObjectDisposedException: Cannot access a closed Stream.
   at System.IO.MemoryStream.Write(Byte[] buffer, Int32 offset, Int32 count)
   at System.Xml.XmlUtf8RawTextWriter.FlushBuffer()
   at System.Xml.XmlUtf8RawTextWriter.Flush()
   at System.Xml.XmlWellFormedWriter.Close()
   at System.Xml.XmlWriter.Dispose(Boolean disposing)
   at System.Xml.XmlWriter.Dispose()

但是如果我删除 XmlWriter 周围的 using 块...它会起作用。

string text = null;

using (var memoryStream = new MemoryStream())
{
    var xmlWriter = XmlWriter.Create(memoryStream));

    xmlWriter.WriteStartDocument();
    xmlWriter.WriteStartElement("Foo");
    xmlWriter.WriteEndElement();
    xmlWriter.WriteEndDocument();
    xmlWriter.Flush();

    memoryStream.Position = 0;

    using (var streamReader = new StreamReader(memoryStream))
    {
        text = streamReader.ReadToEnd();
    }
}

await context.Response.WriteAsync(text, Encoding.UTF8);

我无法理解为什么会发生这种情况以及为什么我不能在 TextWriter 周围使用 using 块。用 StreamWriter 完成的相同步骤工作得很好,那么 XmlWriter 有什么区别?我假设在使用块结束之前不会关闭流。我的思路哪里出了问题?

默认情况下StreamReader 处理底层流。如果在释放 StreamReader 后需要它(并且 XmlWriter 使用它来执行 Flush),您可以将 leaveOpen ctor 参数设置为 true :

using (var streamReader = new StreamReader(memoryStream, leaveOpen: true))

查看堆栈跟踪:异常发生在 XmlWriter 被处置时,在其块的末尾。它被处理,它刷新,它尝试写入底层流并砰! - 某些东西已经处理了底层流。

StreamReader 的块在 XmlWriter 的块之前结束,并且此处置也(默认情况下)处置底层流。这是唯一可以做到的事情。

它在其他情况下工作的原因是 StreamWriter 不能在处理时刷新(或者更有可能 - 它 不需要 刷新因为它的缓冲区要么是空的,要么是 non-existing)。这意味着 StreamReader 在它之前已经介入并不重要。

在最后一种情况下它起作用是因为 XmlWriter 没有被处理,并且根本没有试图刷新(但是,它显然不需要,因为你已经手动刷新,输出正确 - 我想知道为什么它的 dispose 方法坚持要刷新?但它正在做)

这里一般的解决办法是更有选择性地使用using。你可以完全错过 StreamReader,也可以将 'leaveOpen' 参数传递给它。

我通常的方法是只担心处理流,因为它始终是链接到需要处理的非托管资源的流。读者和作家的处置实际上只是一种简洁地处置流的好方法。