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

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

Схема, показывающая, как копируются значения и ссылочные типы.

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

Схема, показывающая, как ссылочные типы передаются по значению и по ссылке.

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

Форматированные Blittable классы

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

Замечание

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

Форматированные классы, отличные от Blittable

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

System.String и System.Text.StringBuilder

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

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

Осторожность

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

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

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

См. также