.net MemoryStream上的GetBuffer()什么时候有用?

.net MemoryStream上的GetBuffer()什么时候有用?,.net,memorystream,getbuffer,.net,Memorystream,Getbuffer,我知道C#/.NET中MemoryStream上的GetBuffer()必须小心使用,因为正如文档所描述的,末尾可能有未使用的字节,因此您必须确保只查看缓冲区中的第一个MemoryStream.Length字节 但是昨天我遇到了一个例子,缓冲区开头的字节是垃圾!实际上,如果您使用类似reflector的工具并查看ToArray(),您可以看到: public virtual byte[] ToArray() { byte[] dst = new byte[this._length - t

我知道C#/.NET中MemoryStream上的
GetBuffer()
必须小心使用,因为正如文档所描述的,末尾可能有未使用的字节,因此您必须确保只查看缓冲区中的第一个MemoryStream.Length字节

但是昨天我遇到了一个例子,缓冲区开头的字节是垃圾!实际上,如果您使用类似reflector的工具并查看
ToArray()
,您可以看到:

public virtual byte[] ToArray()
{
    byte[] dst = new byte[this._length - this._origin];
    Buffer.InternalBlockCopy(this._buffer, this._origin, dst, 0,
        this._length - this._origin);
    return dst;
}
因此,要处理由
GetBuffer()
返回的缓冲区,您确实需要知道其来源。唯一的问题是_origin是私有的,没有办法得到它

因此,我的问题是-如果没有一些关于内存流是如何构造的先验知识(这是什么设置的),那么
GetBuffer()
MemoryStream()
上有什么用

(正是这个构造函数,而且只有这个构造函数,设置了原点-当您希望在字节数组中的某个特定索引开始的字节数组周围有一个MemoryStream时:

public MemoryStream(byte[] buffer, int index, int count, bool writable, bool publiclyVisible)
)

GetBuffer()
始终假定您知道输入字符串的数据的结构(这就是它的用途)。如果要从流中获取数据,应始终使用提供的方法之一(例如
ToArray()

类似这样的东西是可以使用的,但我现在能想到的唯一情况是流中的某个固定结构或虚拟文件系统。例如,在当前位置,您正在读取位于流中的文件的偏移量。然后,基于此流的缓冲区创建一个新的流对象,但其来源不同。这样可以避免复制新对象的全部数据,从而节省大量内存。这样可以避免将初始缓冲区作为引用随身携带,因为您始终可以再次检索它。

ToArray()是GetBuffer()的替代方法。但是,ToArray()会在内存中创建对象的副本。如果字节数超过80000,对象将被放入大对象堆(LOH)。到目前为止没有什么特别的。然而,GC不能很好地处理LOH和其中的对象(内存并没有像您期望的那样被释放)。因此,可能会发生OutOfMemoryException。解决方案是调用GC.Collect()以收集这些对象,或者使用GetBuffer()并创建几个较小(小于80000字节)的对象-这些对象不会进入LOH,内存将按照GC的预期释放

存在第三个(更好)选项,即仅使用流,例如从MemoryStream读取所有字节,并直接将它们写入HttpResponse.OutputStream(再次使用小于80000字节的字节数组作为缓冲区)。然而,这并不总是可能的(就像我的情况一样)

作为总结,我们可以说,当不需要对象的内存副本时,您必须避免ToArray(),在这种情况下,GetBuffer()可能会派上用场,但可能不是最佳解决方案。

答案在中,您可能错过了它

创建
内存流时,如果不提供字节数组(
字节[]
):

它会创建一个初始化为零的可扩展容量

换句话说,当对流进行
Write
调用时,MemoryStream将引用具有适当大小的
字节[]

因此,使用
GetBuffer()
可以直接访问底层数组并读取它

当您在不知道流大小的情况下接收流时,此可能非常有用。如果接收到的流通常很大,那么调用
GetBuffer()
比调用
ToArray()
要快得多,后者会在引擎盖下复制数据,请参见下文

要仅获取缓冲区中的数据,请使用ToArray方法; 但是,ToArray会在内存中创建数据的副本


我想知道您在开始时调用GetBuffer()获取垃圾数据的时间点,可能是在两次
调用之间,第一次调用的数据会被垃圾收集,但我不确定是否会发生这种情况。

如果您使用的是采用
数组分段的低级API,这可能会很有用,比如。您可以创建一个段,而不是调用
ToArray
,这将创建阵列的另一个副本:

var segment=new ArraySegment<byte>(stream.GetBuffer(), 0, stream.Position);
var segment=newarraysegment(stream.GetBuffer(),0,stream.Position);

然后将其传递给
Send
方法。对于大数据,这将避免分配新数组并将其复制到其中,这可能会很昂贵。

如果确实要访问内部_origin值,可以使用MemoryStream.Seek(0,SeekOrigin.Begin)调用。返回值将与原始值完全相同。

.NET4.6有一个新的API,其精神与
.GetBuffer()
类似。如果可以,此方法将返回包含
\u origin
信息的


有关何时
.TryGetBuffer()
将返回true并用有用信息填充out参数的详细信息,请参阅。

GetBuffer
最重要的一点是,除了不创建数据副本之外,它返回一个包含未使用字节的数组:

请注意,缓冲区包含可能未使用的已分配字节。例如,如果将字符串“test”写入MemoryStream对象,则从GetBuffer返回的缓冲区长度为256,而不是4,未使用252字节。要仅获取缓冲区中的数据,请使用ToArray方法;但是,ToArray会在内存中创建数据的副本

因此,如果您确实希望避免由于内存限制而创建副本,那么必须小心不要通过线路从
GetBuffer
发送整个数组,也不要将其转储到文件或附件中,因为每当缓冲区被填满时,它都会以2的幂增长,并且几乎总是以2的幂增长