Копирование и закрепление

При маршалинге данных маршализатор взаимодействия может копировать или закреплять данные, которые маршалируются. В ходе этого процесса копия данных переносится из одного расположения в памяти в другое. На следующем рисунке показаны различия между копированием типа значения и типа, передаваемого по ссылке, из управляемой памяти в неуправляемую.

Diagram that shows how value and reference types are copied.

Аргументы метода, передаваемые по значению, маршалируются в неуправляемый код в виде значений в стеке. Процесс копирования является прямым. Аргументы, передаваемые по ссылке, передаются как указатели на стек. Ссылочные типы также могут передаваться по значению и по ссылке. Как показано на следующем рисунке, ссылочные типы, передаваемые по значению, либо копируются, либо закрепляются.

Diagram showing reference types passed by value and by reference.

При закреплении данные временно блокируются в текущем положении в памяти, в результате чего исключается их перемещение стандартным сборщиком мусора общеязыковой среды выполнения. Маршаллизатор закрепляет данные, чтобы сократить затраты на копирование и повысить производительность. Тип данных определяет, копируется ли он или закрепляется во время процесса маршаллинга. Закрепление выполняется автоматически во время маршаллинга для таких объектов, как String, однако, можно также вручную закрепить память с помощью GCHandle класса.

Форматированные непреобразуемые классы

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

Примечание.

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

Форматированные преобразуемые классы

Форматированные преобразуемые классы имеют фиксированную структуру (форматирование), но разное представление данных в управляемой и неуправляемой памяти. В следующих ситуациях может потребоваться преобразование данных:

  • Если класс, не допускающий переключения, маршалируется по значению, вызывающий получает указатель на копию структуры данных.

  • Если класс, не допускающий переключения, маршалируется по ссылке, вызывающий получает указатель на указатель на копию структуры данных.

  • InAttribute Если атрибут задан, эта копия всегда инициализирована с состоянием экземпляра, маршалинг по мере необходимости.

  • OutAttribute Если атрибут задан, состояние всегда копируется обратно в экземпляр при возврате, маршалинг по мере необходимости.

  • Если установлены одновременно атрибуты InAttribute и OutAttribute, обе операции копирования выполняются обязательно. Если любой атрибут опущен, маршаллизатор может оптимизироваться, исключив любую копию.

Ссылочные типы

Ссылочные типы могут передаваться по значению или по ссылке. При передаче по значению в стек передается указатель на тип. При передаче по ссылке в стек передается указатель на указатель на тип.

Ссылочные типы имеют следующие условные характеристики:

  • Если ссылочный тип передается по значению и содержит члены преобразуемых типов, типы преобразуются дважды:

    • Когда аргумент передается в неуправляемый код.

    • При возврате из вызова.

    Чтобы избежать ненужных копирования и преобразования, эти типы маршалируются как в параметрах. Чтобы вызывающий объект мог видеть изменения, выполненные вызывающим объектом, необходимо явно применить атрибуты InAttribute и OutAttribute к аргументу.

  • Если ссылочный тип передается по значению, и он содержит только члены ситуабельных типов, он может быть закреплен во время маршаллинга и любые изменения, внесенные в члены типа вызывающим пользователем, отображаются вызывающим элементом. Чтобы реализовать такое поведение, явно примените атрибуты InAttribute и OutAttribute. Без этих атрибутов направление маршализатор не экспортирует данные направления в библиотеку типов (экспортируется как In, которая является стандартной) и может привести к проблемам с маршаллинга между квартирами COM.

  • Если ссылочный тип передается по ссылке, он будет маршалирован как in/Out по умолчанию.

System.String и System.Text.StringBuilder

Когда данные маршалируются в неуправляемый код по значению или по ссылке, маршаллизатор обычно копирует данные в дополнительный буфер (возможно, преобразовывая наборы символов во время копирования) и передает ссылку на буфер вызывающему объекту. Если ссылка не является объектом BSTR (для выделения используется SysAllocString), выделение памяти всегда осуществляется с помощью CoTaskMemAlloc.

В качестве оптимизации при StringStringBuilder маршале по значению (например, строке символов Юникода) маршаллизатор передает вызывающему объекту прямой указатель на управляемые строки во внутреннем буфере Юникода, а не копирует его в новый буфер.

Внимание

Когда строка передается по значению, вызывающий объект никогда не должен изменять ссылку, передаваемую маршаллером. Это может привести к повреждению управляемой кучи.

System.String При передаче по ссылке маршаллизатор копирует содержимое строки в дополнительный буфер перед вызовом. После этого содержимое буфера копируется в новую строку при возврате из вызова. Такой подход гарантирует, что неизменяемая управляемая строка останется без изменений.

System.Text.StringBuilder При передаче по значению маршаллизатор передает ссылку на временную копию внутреннего буфера StringBuilder вызывающей объекту. Вызывающий и вызываемый объекты должны согласовывать размер буфера. Вызывающий объект отвечает за создание буфера StringBuilder соответствующей длины. Вызываемый объект должен принимать необходимые меры предосторожности, чтобы предотвратить переполнение буфера. StringBuilder является исключением из правила, которое ссылочные типы, передаваемые значением, передаются в качестве In параметров по умолчанию. StringBuilder всегда передается как In/Out.

См. также