Нужно ли связывать структуру при копировании байтов из памяти

Я определил структуру в С#, чтобы зеркалировать собственную структуру данных и использовал 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.

Да, вы должны это сделать - и ваш код выглядит хорошо для меня.

Вы не знали бы, где будет жить ваша структура. Он может быть частью другой структуры объекта, поэтому находится в куче, или это может быть локальная переменная, которая, скорее всего, будет в стеке. Если он находится в куче, тогда вам нужно закрепить его.