Нужно ли связывать структуру при копировании байтов из памяти
Я определил структуру в С#, чтобы зеркалировать собственную структуру данных и использовал StructLayout of Sequential. Чтобы преобразовать структуру в 12 байтов (3x4 байта), требуемых методом Socket IOControl, я использую Marshal.Copy для копирования байтов в массив.
Поскольку структура содержит только типы значений, нужно ли привязать структуру до того, как я сделаю копию? Я знаю, что GC уплотняет кучу, и поэтому mem-адрес ссылочных типов может меняться во время GC. То же самое относится к типам присвоенных стека значений?
Текущая версия, которая включает операцию вывода, выглядит следующим образом:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct TcpKeepAliveConfiguration
{
public uint DoUseTcpKeepAlives;
public uint IdleTimeMilliseconds;
public uint KeepAlivePacketInterval;
public byte[] ToByteArray()
{
byte[] bytes = new byte[Marshal.SizeOf(typeof(TcpKeepAliveConfiguration))];
GCHandle pinStructure = GCHandle.Alloc(this, GCHandleType.Pinned);
try
{
Marshal.Copy(pinStructure.AddrOfPinnedObject(), bytes, 0, bytes.Length);
return bytes;
}
finally
{
pinStructure.Free();
}
}
}
Любые мысли?
Ответы
Ответ 1
Если ваша структура захвачена, скажем, выражением лямбда, она не будет сохранена в стеке.
Таким образом, я бы рекомендовал вам всегда связывать структуру перед копированием.
Эрик Липперт написал статью о хранилище типов значений, которая может вас заинтересовать.
Ответ 2
Фредерик и Алиостад верны; вы не знаете, где действительно живет "this", и поэтому вы не знаете, разрешено ли сборщику мусора перемещать его или нет.
Я просто хочу указать, что есть эквивалентное решение вашей проблемы, которое может оказаться полезным. Вы также можете решить свою проблему:
public byte[] ToByteArray()
{
byte[] bytes = new byte[Marshal.SizeOf(typeof(TcpKeepAliveConfiguration))];
unsafe
{
fixed (TcpKeepAliveConfiguration* ptr = &this)
{
// now you have pinned "this" and obtained a pointer to it in one step
}
}
}
Оператор "fixed" гарантирует, что во время тела его блока неуправляемый указатель на "this" действителен, поскольку память не может быть перемещена сборщиком мусора. В основном это другой способ написания кода; некоторые люди считают этот способ более легким для чтения.
(Обратите внимание, что вы должны установить флажок "разрешить небезопасную" в Visual Studio или использовать флаг "/unsafe" в командной строке при создании кода, который содержит небезопасный контекст.)
Ответ 3
Измените определение на class
вместо struct
.
Да, вы должны это сделать - и ваш код выглядит хорошо для меня.
Вы не знали бы, где будет жить ваша структура. Он может быть частью другой структуры объекта, поэтому находится в куче, или это может быть локальная переменная, которая, скорее всего, будет в стеке. Если он находится в куче, тогда вам нужно закрепить его.