内存流返回时禁用读取功能

10

我在我的程序中,基本上是读取一个文件,对其进行一些处理,然后将其作为内存流返回给主程序,该内存流将由一个流阅读器处理。所有这些都将由我的主类旁边的一个类处理。

问题是,在另一个类的方法中返回内存流时,“canread”变量被设置为false,从而导致流阅读器初始化失败。

以下是问题发生的示例(虽然在这里我是在另一个类中写入内存流,但当我将其传回时仍会导致相同的错误)。

在名为“Otherclass”的类中:

public static MemoryStream ImportantStreamManipulator()
{
   MemoryStream MemStream = new MemoryStream();

   StreamWriter writer = new StreamWriter(MemStream);
   using (writer)
   {
       //Code that writes stuff to the memorystream via streamwriter

       return MemStream;
   }
}

主程序中的函数调用:

MemoryStream MStream = Otherclass.ImportantStreamManipulator();
StreamReader reader = new StreamReader(MStream);
当我在"return MemStream"上设置断点时,"CanRead"属性仍然为true。一旦我跳出该函数并将返回值写入MStream后,"CanRead"属性就会变为false。这会导致StreamReader抛出异常,表示无法读取MStream(因为该属性指示不可读)。数据已经按照预期存储在流的缓冲区中,但是我只是无法将其取出。
如何设置使得当它返回到我的主函数时,“CanRead”将报告为true?或者我是否误解了MemoryStream的工作原理?我应该如何实现我想要做的事情?
3个回答

30

这是问题:

using (writer)
{
    //Code that writes stuff to the memorystream via streamwriter

    return MemStream;
}

您正在关闭写入器,这会关闭MemoryStream。在这种情况下,您不希望这样做...尽管您确实需要刷新写入器并倒回MemoryStream。只需更改您的代码:

public static MemoryStream ImportantStreamManipulator()
{
   // Probably add a comment here stating that the lack of using statements
   // is deliberate.
   MemoryStream stream = new MemoryStream();

   StreamWriter writer = new StreamWriter(stream);
   // Code that writes stuff to the memorystream via streamwriter

   writer.Flush();
   stream.Position = 0;
   return stream;
}

Jon,如果在使用stream之前writer被垃圾回收了会发生什么? - Kevin Brock
2
@KevinBrock:然后它就被垃圾回收了,任何缓冲数据都会丢失。据我所知,StreamWriter没有终结器。 - Jon Skeet
那就是它了。我原以为返回它会在使用之前将一个非关闭版本发送出去,导致Streamwriter未被释放。我想我当时认为返回值会返回MemoryStream的副本而不是引用。我也认为通过返回方式离开ImportantStreamManipulator方法会自动处理Streamwriter的释放,所以我不确定这是否可行。 Streamwriter会在程序中的某个时间点由GC进行释放,还是只有在我释放MemoryStream或关闭程序时才会释放? - Xantham
@Xantham: 你不需要手动释放 StreamWriter。它没有任何非托管资源。在最后一次使用后,它会在任何时候被垃圾回收 - 可能是在 Flush 调用之后直接清理。 - Jon Skeet

2

0

正如其他人所说,问题在于当StreamWriter关闭时,Stream也被关闭了。解决这个问题的一种可能的方法是返回一个字节数组而不是MemoryStream。这避免了可能运行时间长的对象必须由垃圾收集器处理的情况。

public static void Main()
{
    OutputData(GetData());
}

public static byte[] GetData()
{
    byte[] binaryData = null;

    using (MemoryStream ms = new MemoryStream())
    using (StreamWriter sw = new StreamWriter(ms))
    {
        string data = "My test data is really great!";

        sw.Write(data);
        sw.Flush();

        binaryData = ms.ToArray();
    }

    return binaryData;
}

public static void OutputData(byte[] binaryData)
{
    using (MemoryStream ms = new MemoryStream(binaryData))
    using (StreamReader sr = new StreamReader(ms))
    {
        Console.WriteLine(sr.ReadToEnd());
    }
}

另一种方法是在返回之前将Stream复制到另一个流中。但是,这仍然存在问题,即使用StreamReader对其进行随后的访问将关闭该流。
public static void RunSnippet()
{
    OutputData(GetData());
}

public static MemoryStream GetData()
{
    MemoryStream outputStream = new MemoryStream();

    using (MemoryStream ms = new MemoryStream())
    using (StreamWriter sw = new StreamWriter(ms))
    {
        string data = "My test data is really great!";

        sw.Write(data);
        sw.Flush();

        ms.WriteTo(outputStream);
        outputStream.Seek(0, SeekOrigin.Begin);
    }

    return outputStream;
}

public static void OutputData(MemoryStream inputStream)
{
    using (StreamReader sr = new StreamReader(inputStream))
    {
        Console.WriteLine(sr.ReadToEnd());
    }
}   

MemoryStream.ToArray()甚至在底层流被关闭时也可以正常工作。虽然您无法访问已关闭的流,但数据仍然存在。 - Jan

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