OPに同意します。効率に注意を払う必要がある場合があります。API を提供するという例が最善だとは思いません。なぜなら、効率よりも安全性とシンプルさに傾倒する必要があるからです。
ただし、単純な例として、パーサーを作成する場合など、膨大な数のレコードを含む大量の巨大なバイナリ ファイルを処理する場合があります。System.ArraySegment などのメカニズムを使用しないと、パーサーは大量のメモリを消費し、無数の新しいデータ要素を作成し、すべてのメモリをコピーし、ヒープから完全に断片化することで大幅に速度が低下します。これは非常に現実的なパフォーマンスの問題です。私はこの種のパーサーを通信用に常に作成しており、データベースに解析する必要がある可変長のバイナリ構造を持つ多くのスイッチのそれぞれから、いくつかのカテゴリのそれぞれで 1 日に何百万ものレコードを生成します。
System.ArraySegment メカニズムを使用するのではなく、レコードごとに新しい構造体のコピーを作成すると、解析が大幅に高速化され、パーサーのピーク時のメモリ消費量が大幅に削減されます。サーバーは複数のパーサーを実行し、それらを頻繁に実行し、速度とメモリの節約 = 解析専用のプロセッサをそれほど多く持つ必要がないため、非常に現実的なコスト削減になるため、これらは非常に現実的な利点です。
System.Array セグメントは非常に使いやすいです。固定長のヘッダーと可変長のレコード サイズ (明らかな例外制御は削除されています) を持つレコードでいっぱいの典型的な大きなバイナリ ファイルで個々のレコードを追跡する基本的な方法を提供する簡単な例を次に示します。
public struct MyRecord
{
ArraySegment<byte> header;
ArraySegment<byte> data;
}
public class Parser
{
const int HEADER_SIZE = 10;
const int HDR_OFS_REC_TYPE = 0;
const int HDR_OFS_REC_LEN = 4;
byte[] m_fileData;
List<MyRecord> records = new List<MyRecord>();
bool Parse(FileStream fs)
{
int fileLen = (int)fs.FileLength;
m_fileData = new byte[fileLen];
fs.Read(m_fileData, 0, fileLen);
fs.Close();
fs.Dispose();
int offset = 0;
while (offset + HEADER_SIZE < fileLen)
{
int recType = (int)m_fileData[offset];
switch (recType) { /*puke if not a recognized type*/ }
int varDataLen = ((int)m_fileData[offset + HDR_OFS_REC_LEN]) * 256
+ (int)m_fileData[offset + HDR_OFS_REC_LEN + 1];
if (offset + varDataLen > fileLen) { /*puke as file has odd bytes at end*/}
MyRecord rec = new MyRecord();
rec.header = new ArraySegment(m_fileData, offset, HEADER_SIZE);
rec.data = new ArraySegment(m_fileData, offset + HEADER_SIZE,
varDataLen);
records.Add(rec);
offset += HEADER_SIZE + varDataLen;
}
}
}
上記の例では、ファイル内の各レコードの ArraySegments を含むリストが得られますが、ファイルごとに 1 つの大きな配列にすべての実際のデータが配置されています。唯一のオーバーヘッドは、レコードごとの MyRecord 構造体の 2 つの配列セグメントです。レコードを処理するとき、MyRecord.header.Array および MyRecord.data.Array プロパティを使用して、各レコードの要素を独自の byte[] コピーであるかのように操作できます。