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