41

C#/。NETのMemoryStreamでは注意して使用する必要があることを知っています。これGetBuffer()は、ドキュメントで説明されているように、最後に未使用のバイトが存在する可能性があるため、最初のMemoryStreamのみを確認する必要があるためです。バッファ内の.Lengthバイト。

しかし、昨日、バッファの先頭のバイトがジャンクであるというケースに遭遇しました。実際、リフレクターなどのツールを使用してを見ると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を知っている必要があります。唯一の問題は、_originがプライベートであり、それを取得する方法がないことです...

だから私の質問は-MemoryStreamがどのように構築されたか(これが_originを設定するものです)についての事前の知識がなくてもGetBuffer()、どのような用途がありますか?MemoryStream()

(原点を設定するのはこのコンストラクターであり、このコンストラクターのみです-バイト配列内の特定のインデックスで始まるバイト配列の周りにMemoryStreamが必要な場合:

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

)。

4

7 に答える 7

21

答えはGetBuffer()MSDNドキュメントにあります。見逃しているかもしれません。

MemoryStreamバイト配列を指定せずにを作成する場合( byte[]):

ゼロに初期化された拡張可能な容量を作成します。

つまり、MemoryStreamは、Streamで呼び出しが行われるbyte[]ときに、適切なサイズのを参照します。Write

したがって、をGetBuffer()使用すると、基になる配列に直接アクセスして読み取ることができます。

これは、ストリームのサイズを知らなくてもストリームを受信する状況にある場合に役立ちます。受信したストリームが通常非常に大きい場合は、内部でデータをコピーする呼び出しGetBuffer()よりも呼び出しの方がはるかに高速です。以下を参照してください。ToArray()

バッファ内のデータのみを取得するには、ToArrayメソッドを使用します。ただし、ToArrayはデータのコピーをメモリに作成します。

最初にジャンクデータを取得するためにGetBuffer()を呼び出したのはどの時点かと思います。最初のWrite呼び出しのデータがガベージコレクションされたのは、2回の呼び出しの間にある可能性がありますが、それが発生するかどうかはわかりません。

于 2013-05-16T21:44:22.107 に答える
17

内部の_origin値に本当にアクセスしたい場合は、MemoryStream.Seek(0、SeekOrigin.Begin)呼び出しを使用できます。戻り値は正確に_origin値になります。

于 2015-05-06T23:27:01.003 に答える
14

.NET 4.6には、bool MemoryStream.TryGetBuffer(out ArraySegment<byte> buffer)精神的にはに似た新しいAPIがあります.GetBuffer()。このメソッドは、可能であれば情報ArraySegmentを含むを返します。_origin

いつtrueが返され、outパラメータに有用な情報が入力されるかについての詳細は、この質問を参照してください。.TryGetBuffer()

于 2015-06-12T16:50:46.243 に答える
12

ToArray()はGetBuffer()の代替です。ただし、ToArray()はメモリ内のオブジェクトのコピーを作成します。バイトが80000を超える場合、オブジェクトはラージオブジェクトヒープ(LOH)に配置されます。これまでのところ、派手なものは何もありません。ただし、GCはLOHとその中のオブジェクトをうまく処理しません(メモリは期待どおりに解放されません)。このため、OutOfMemoryExceptionが発生する可能性があります。解決策は、GC.Collect()を呼び出してそれらのオブジェクトを収集するか、GetBuffer()を使用していくつかの小さい(80000バイト未満)オブジェクトを作成することです-それらはLOHに移動せず、メモリは期待どおりに解放されますGCによって。

3番目の(より良い)オプションがあります。これは、ストリームのみを使用することです。たとえば、MemoryStreamからすべてのバイトを読み取り、それらをHttpResponse.OutputStreamに直接書き込みます(バイト配列<80000バイトをバッファーとして使用)。ただし、これが常に可能であるとは限りません(私の場合のように)。

要約すると、オブジェクトのメモリ内コピーが望ましくない場合は、ToArray()を回避する必要があり、その場合はGetBuffer()が便利ですが、最善の解決策ではない可能性があります。

于 2012-11-20T16:16:11.797 に答える
9

Socket.SendArraySegmentなどのをとる低レベルのAPIを使用している場合に便利です。配列の別のコピーを作成する呼び出しではなく、セグメントを作成できます。ToArray

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

Send次に、それをメソッドに渡します。大きなデータの場合、これにより、新しい配列を割り当ててそこにコピーする必要がなくなり、コストがかかる可能性があります。

于 2015-05-01T13:41:27.383 に答える
5

データのコピーを作成しないことを除いて、 GetBuffer MSDNドキュメントの最も重要な点は、未使用のバイトを持つ配列を返すことです。

バッファには、使用されていない可能性のある割り当てられたバイトが含まれていることに注意してください。たとえば、文字列「test」がMemoryStreamオブジェクトに書き込まれる場合、GetBufferから返されるバッファの長さは4ではなく256で、252バイトは未使用です。バッファ内のデータのみを取得するには、ToArrayメソッドを使用します。ただし、ToArrayはデータのコピーをメモリに作成します。

したがって、メモリの制約のためにコピーを作成することを本当に避けたい場合は、アレイ全体をネットワーク経由で送信しGetBufferたり、ファイルや添付ファイルにダンプしたりしないように注意する必要があります。いっぱいになり、ほとんどの場合、最後に未使用のバイトがたくさんあります。

于 2016-11-01T10:27:33.023 に答える
4

GetBuffer()文字列に入力されるデータの構造を知っていることを常に前提としています(それがその使用法です)。ストリームからデータを取得する場合は、常に提供されているメソッドの1つを使用する必要があります(例ToArray())。

このようなものを使用することもできますが、現時点で考えられるのは、ストリーム内にある固定構造または仮想ファイルシステムだけです。たとえば、現在の位置で、ストリーム内にあるファイルのオフセットを読み取っています。次に、このストリームのバッファに基づいて、異なる。を使用して新しいストリームオブジェクトを作成します_origin。これにより、新しいオブジェクトのデータ全体をコピーする必要がなくなり、大量のメモリを節約できる可能性があります。これにより、いつでも再度取得できるため、初期バッファーを参照として持ち歩く必要がなくなります。

于 2012-10-24T16:55:52.370 に答える