【问题标题】:Disposing MemoryStreams and GZipStreams处理 MemoryStreams 和 GZipStreams
【发布时间】:2017-01-18 16:28:23
【问题描述】:

我想在序列化时压缩 ProtoBuffer 对象并在反序列化时解压缩。不幸的是,C# stdlib 只提供了对流而不是字节 [] 工作的压缩例程,这使得它比函数调用更冗长一些。到目前为止我的代码:

class MyObject{
 public string P1 {get; set;}
 public string P2 {get; set;}
 // ...

 public byte[] Serialize(){
   var builder = new BinaryFormat.MyObject.Builder();
   builder.SetP1(P1);
   builder.SetP2(P2);
   // ...

   // object is now build, let's compress it.
   var ms = new MemoryStream();
   // Without this using, the serialisatoin/deserialisation Tests fail
   using (var gz = new GZipStream(ms, CompressionMode.Compress))
   {
     builder.Build().WriteTo(gz);
   }
   return ms.ToArray();
 }

 public void Deserialize(byte[] data)
 {
   var ms = new MemoryStream();
   // Here, Tests work, even when the "using" is left out, like this:
   (new GZipStream(new MemoryStream(data), CompressionMode.Decompress)).CopyTo(ms);
   var msg = BinaryFormat.MachineInfo.ParseFrom(ms.ToArray());

   P1 = msg.P1;
   P2 = msg.P2;
   // ...
 }
}

在处理流时,似乎必须手动处理对象的处理。我想知道为什么会这样,我希望 GZipStream 是完全托管的代码。而且我想知道 Deserialize 是否只是偶然起作用,我是否也应该处理 MemoryStreams。

我知道我可以通过简单地使用第三方压缩库来解决这个问题,但这有点超出了这个问题的重点。

【问题讨论】:

  • 如果我没记错的话,MemoryStream 在拥有对象时被处理掉了,在这种情况下,GZipStream 被处理掉了。或者那可能只是为了Images...
  • 好吧,把它当作一个学习曲线,如果有东西实现了IDisposable,它应该被处理掉,最好使用using。编写代码以正确使用和处理对象,而您一开始就不会发现问题。
  • @ScottChamberlain 这更像是一个笼统的评论,因为总是有边缘情况,但它们超出了问题的范围
  • 编译器不应该警告我这个陷阱吗?对我来说,GZipStream 甚至实现了 IDisposable 并不明显。这个 BTW 在 GC 语言中感觉很奇怪。

标签: c# idisposable gzipstream


【解决方案1】:

GZipStream 需要被释放,以便将其最终的压缩块从其缓冲区刷新到其底层流,它还会在您传入的流上调用 dispose,除非您使用重载 that takes in a bool and you pass in false

如果您使用未处理 MemoryStream 的重载,则处理 MemoryStream 并不重要,因为它不会在任何地方写入其内部缓冲区。它唯一要做的就是设置一些标志并设置一个 Task 对象为 null,这样如果流的生命周期长于处置点,它就可以更快地被 GC。

    protected override void Dispose(bool disposing)
    {
        try {
            if (disposing) {
                _isOpen = false;
                _writable = false;
                _expandable = false;
                // Don't set buffer to null - allow TryGetBuffer, GetBuffer & ToArray to work.
#if FEATURE_ASYNC_IO
                _lastReadTask = null;
#endif
            }
        }
        finally {
            // Call base.Close() to cleanup async IO resources
            base.Dispose(disposing);
        }
    }

此外,虽然注释说“调用 base.Close() 来清理异步 IO 资源”,但 Stream 类中的基本 dispose 函数根本没有任何作用。

    protected virtual void Dispose(bool disposing)
    {
        // Note: Never change this to call other virtual methods on Stream
        // like Write, since the state on subclasses has already been 
        // torn down.  This is the last code to run on cleanup for a stream.
    }

话虽如此,在解压 GZipStream 时,您可能会因为不释放 MemoryStream 的相同原因而侥幸逃脱,因为在解压时不会在任何地方缓冲字节,因此无需刷新任何缓冲区。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-27
    • 1970-01-01
    • 2020-10-19
    • 2015-05-20
    • 2023-03-31
    • 2012-03-11
    相关资源
    最近更新 更多