处理 MemoryStreams 和 GZipStreams

Disposing MemoryStreams and GZipStreams

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

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。

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

GZipStream 需要处理,以便将其最终压缩块从其缓冲区冲出到其底层流,除非您使用重载 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);
        }
    }

此外,尽管评论说 "Call base.Close() to cleanup async IO resources" 来自 Stream class 的基本处理函数根本不执行任何操作。

    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 相同的原因而不处置它,解压时它不会在任何地方缓冲字节,因此无需刷新任何缓冲区.