有多种方法可以阅读这个问题,但让我们从显而易见的方式开始,它的编写方式:
这种在方法中使用“使用”的方式是否可能会导致 GC 删除内存流,在线程读取数据之前让我们说在数据进入 FIFO 后几秒钟或几分钟后?
不,这不会是一个问题。如果您能够在对 的调用中读取数据.ToArray()
,那么您已经拥有数据的副本。如果 GC 稍后收集流,则该数组将继续存在。需要明确的是,关于 GC,如果您可以在调用处读取流内部的良好副本.ToArray()
,那么该数组之后就可以了。根据文档,您获得的是内部数据的副本,而不是对其的引用,即使如此,如果您引用了某些内部数据结构,GC 将无法收集它。
但是,另一种解释可能是:这段代码有问题吗?
好吧,是的,也不是。
当前的实现BinaryWriter
将在编写器实例被处置时处置底层流。这意味着MemoryStream
将被处置。
让我复制您的代码并添加一些注释:
public byte[] ByteArraySerialize()
{
using (MemoryStream m = new MemoryStream())
{
using (BinaryWriter writer = new BinaryWriter(m))
{
writer.Write((int)this.Opcode);
writer.Write(this.Data);
}
// m is really disposed here
return m.ToArray();
}
}
这有什么不同吗?嗯,不。在当前的实现中,处理内存流不会以任何方式丢弃它。但是对于当前的实现或其未来没有任何保证,这是未记录的行为。如果您希望此代码对于 .NET 的未来版本或修补程序而言是稳定且值得信赖的,我不会这样写。
因此,我不会使用这种方式。我将重写代码如下:
using (MemoryStream m = new MemoryStream())
using (BinaryWriter writer = new BinaryWriter(m))
{
writer.Write((int)this.Opcode);
writer.Write(this.Data);
writer.Flush();
return m.ToArray();
}
这将要求编写器刷新所有数据,然后在释放该实例之前复制内存流的内部数组。
要么,要么使用重载的构造函数并要求编写者保持流打开:
using (MemoryStream m = new MemoryStream())
{
using (BinaryWriter writer = new BinaryWriter(m, Encoding.UTF8, true))
{
writer.Write((int)this.Opcode);
writer.Write(this.Data);
}
// m is no longer disposed here
return m.ToArray();
}