Подтвердить что ты не робот

Когда GetBuffer() на MemoryStream когда-либо полезен?

Я знал, что GetBuffer() на MemoryStream в С#/.NET должен использоваться с осторожностью, потому что, поскольку документы описывают здесь, в конце могут быть неиспользуемые байты, поэтому вы должны обязательно смотреть только на первые байты 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 является закрытым, и нет способа добраться до него...

Итак, мой вопрос: какое использование GetBuffer() на MemoryStream() без каких-либо априорных знаний о том, как был создан MemoryStream (что и задает _origin)?

(Именно этот конструктор и только этот конструктор устанавливает начало координат - если вы хотите, чтобы MemoryStream вокруг байтового массива начинался с определенного индекса в массиве байтов:

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

)

4b9b3361

Ответ 1

Если вы действительно хотите получить доступ к внутреннему значению _origin Value, вы можете использовать вызов MemoryStream.Seek(0, SeekOrigin.Begin). Возвращаемое значение будет точно значением _origin.

Ответ 2

Ответ в документе GetBuffer() MSDN, возможно, вы его пропустили.

Когда вы создаете MemoryStream без предоставления байтового массива (byte[]):

он создает расширяемую емкость, инициализированную до нуля.

Другими словами, MemoryStream будет ссылаться на byte[] с надлежащим размером, когда будет сделан вызов Write в потоке.

Таким образом, с помощью GetBuffer() вы можете напрямую обращаться к базовому массиву и читать его.

Это может быть полезно, когда вы находитесь в ситуации, когда вы будете получать поток, не зная его размера. Если получаемый поток обычно очень большой, вызов GetBuffer() будет намного быстрее, чем вызов ToArray(), который копирует данные изнутри, см. ниже.

Чтобы получить только данные в буфере, используйте метод ToArray; однако ToArray создает копию данных в памяти.

Интересно, в какой момент вы могли вызвать GetBuffer() для получения ненужных данных в начале, это может быть между двумя вызовами Write, когда данные из первого были бы собраны мусором, но я не уверен, что это могло случиться.

Ответ 3

ToArray() является альтернативой GetBuffer(). Однако ToArray() делает копию объекта в памяти. Если байты больше 80000, объект будет помещен в кучу больших объектов (LOH). Пока ничего необычного. Однако GC не очень хорошо обрабатывает LOH и объекты в нем (память не освобождается, как вы ожидаете). Из-за этого может возникнуть исключение OutOfMemoryException. Решение состоит в том, чтобы либо вызвать GC.Collect(), чтобы эти объекты были собраны, либо использовать GetBuffer(), и создать несколько объектов меньшего размера (менее 80000 байт) - они не подойдут к LOH, и память будет освобождена, как ожидалось по GC.

Существует третий (лучший) вариант, который должен использовать только потоки, например. прочитайте все байты из MemoryStream и напрямую напишите их в HttpResponse.OutputStream(используя снова байтовый массив и 80000 байт в качестве буфера). Однако это не всегда возможно (как это было в моем случае).

В качестве резюме можно сказать, что, когда копия объекта в памяти не нужна, вам придется избегать ToArray(), и в тех случаях GetBuffer() может пригодиться, но может и не быть лучшим решением.

Ответ 4

В .NET 4.6 появился новый API, bool MemoryStream.TryGetBuffer(out ArraySegment<byte> buffer), который по духу похож на .GetBuffer(). Этот метод возвращает ArraySegment, который включает информацию _origin, если это возможно.

См. этот вопрос для получения подробной информации о том, когда .TryGetBuffer() вернет true и заполнит выходной параметр полезной информацией.

Ответ 5

Это может быть полезно, если вы используете API низкого уровня, который принимает ArraySegment, например Socket.Send. Вместо вызова ToArray, который создаст другую копию массива, вы можете создать сегмент:

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

а затем передать это методу Send. Для больших данных это позволит избежать выделения нового массива и копирования в него, что может быть дорогостоящим.

Ответ 6

GetBuffer() всегда предполагает, что вы знаете структуру данных, подаваемых в строку (и ее использование). Если вы хотите получить данные из потока, вы всегда должны использовать один из предоставленных методов (например, ToArray()).

Может быть что-то подобное, но только тот случай, о котором я мог думать сейчас, - это фиксированная структура или виртуальная файловая система, сидящая в потоке. Например, в вашей текущей позиции вы читаете смещение для файла, сидящего внутри потока. Затем вы создаете новый объект потока на основе этого буфера потока, но с другим _origin. Это избавит вас от копирования всех данных для нового объекта, что может позволить вам сэкономить много памяти. Это избавит вас от переноса исходного буфера в качестве ссылки с вами, потому что вы всегда можете восстановить его еще раз.

Ответ 7

Самый важный момент из GetBuffer документации MSDN, за исключением того, что он не создает копию данных, заключается в том, что он возвращает массив с неиспользуемыми байтами:

Обратите внимание, что буфер содержит выделенные байты, которые могут быть неиспользуемы. Например, если строка "test" записывается в объект MemoryStream, длина буфера, возвращаемого из GetBuffer, равна 256, а не 4, а 252 байта не используется. Чтобы получить только данные в буфере, используйте метод ToArray; однако ToArray создает копию данных в памяти.

Итак, если вы действительно хотите избежать создания копии из-за ограничений памяти, вы должны быть осторожны, чтобы не отправлять весь массив из GetBuffer по проводке или сбрасывать его в файл или вложение, поскольку этот буфер растет на полномочия 2 при каждом заполнении и почти всегда имеют много неиспользуемых байтов в конце.

Ответ 8

GetBuffer() чрезвычайно полезен, если вам нужно записать двоичные данные или двоичные файлы.

Например, скажите, что я читаю данные из БД & затем вы хотите записать часть этого в двоичные файлы, вы можете сначала записать данные в буфер при выполнении итерации & затем записать весь буфер в файл за один раз, избегая так много циклов ввода-вывода, чтобы не допустить запись данных непосредственно в файлы во время каждой итерации.

Позвольте мне привести вам пример в C#:

String query = "Select Id, Name from table1";
SqlCommand cmd = new SqlCommand(query, con);
SqlDataAdapter da = new SqlDataAdapter(cmd);
DataTable dt = new DataTable();
da.Fill(dt);

MemoryStream memStream = new MemoryStream(10 * 1024 * 1024) // 10 Mb of membuffer
BinaryWriter memWtr = new BinaryWriter(memStream);

BinaryWriter wtrFile = new BinaryWriter(filePath);// file where you want to write your data eventually

  Foreach (DataRow dr in dt.Rows)
  {
     memWtr.write((int)dr[0]);
     memWtr.write(dr[0].ToString());
  }

//now write whole buffer into File in single call

  wtrFile.write(memStream.GetBuffer(), 0 , memStream.Position);

Надеюсь, это поможет.