Partilhar via


Cópia e fixação

Ao organizar os dados, o marshaller de interoperabilidade pode copiar ou fixar os dados que estão sendo empacotados. A cópia dos dados coloca uma cópia dos dados de um local de memória em outro local de memória. A ilustração a seguir mostra as diferenças entre copiar um tipo de valor e copiar um tipo passado por referência da memória gerenciada para a não gerenciada.

Diagrama que mostra como os tipos de valor e referência são copiados.

Os argumentos de método passados por valor são agrupados em código não gerenciado como valores na pilha. O processo de cópia é direto. Os argumentos passados por referência são passados como ponteiros na pilha. Os tipos de referência também são passados por valor e por referência. Como mostra a ilustração a seguir, os tipos de referência passados pelo valor são copiados ou fixados:

Diagrama mostrando tipos de referência passados por valor e por referência.

A fixação bloqueia temporariamente os dados em seu local de memória atual, evitando assim que eles sejam realocados pelo coletor de lixo do common language runtime. O marshaller fixa dados para reduzir a sobrecarga de cópia e melhorar o desempenho. O tipo de dados determina se eles são copiados ou fixados durante o processo de empacotamento. A fixação é executada automaticamente durante a empacotação de objetos como String, no entanto, você também pode fixar manualmente a memória usando a GCHandle classe.

Classes Blittable formatadas

As classes blittable formatadas têm layout fixo (formatado) e representação de dados comuns na memória gerenciada e não gerenciada. Quando esses tipos exigem empacotamento, um ponteiro para o objeto na pilha é passado diretamente para o destinatário. O destinatário pode modificar o conteúdo do espaço de memória referenciado pelo ponteiro.

Observação

O chamado pode alterar o conteúdo da memória se o parâmetro estiver marcado como Out ou In/Out. Por outro lado, o chamado deve evitar alterar o conteúdo quando o parâmetro é definido para ser marshal como In, que é o padrão para tipos blittable formatados. Modificar um objeto In gera problemas quando a mesma classe é exportada para uma biblioteca de tipos e usada para fazer chamadas entre apartamentos.

Classes formatadas não-Blittable

As classes formatadas não blittable têm layout fixo (formatado), mas a representação de dados é diferente na memória gerenciada e não gerenciada. Os dados podem exigir transformação nas seguintes condições:

  • Se uma classe não blittable for organizada por valor, o destinatário receberá um ponteiro para uma cópia da estrutura de dados.

  • Se uma classe não blittable for empacotada por referência, o destinatário receberá um ponteiro para um ponteiro para uma cópia da estrutura de dados.

  • Se o InAttribute atributo for definido, essa cópia será sempre inicializada com o estado da instância, organizando conforme necessário.

  • Se o OutAttribute atributo for definido, o estado será sempre copiado de volta para a instância no retorno, organizando conforme necessário.

  • Se ambos InAttribute e OutAttribute estiverem definidos, ambas as cópias são necessárias. Se um dos atributos for omitido, o marshaller pode otimizar eliminando qualquer uma das cópias.

Tipos de referência

Os tipos de referência podem ser passados por valor ou por referência. Quando eles são passados pelo valor, um ponteiro para o tipo é passado na pilha. Quando passado por referência, um ponteiro para um ponteiro para o tipo é passado na pilha.

Os tipos de referência têm o seguinte comportamento condicional:

  • Se um tipo de referência for passado por valor e tiver membros de tipos não blittable (não convertíveis diretamente), os tipos são convertidos duas vezes:

    • Quando um argumento é passado para o lado não gerenciado.

    • No regresso da chamada.

    Para evitar cópias e conversões desnecessárias, esses tipos são agrupados como parâmetros In. Deve aplicar explicitamente os atributos InAttribute e OutAttribute a um argumento para que quem chama veja as alterações feitas por quem é chamado.

  • Se um tipo de referência é passado por valor e tem apenas membros de tipos blittable, ele pode ser fixado durante o empacotamento e quaisquer alterações feitas nos membros do tipo pelo destinatário são vistas pelo chamador. Aplique InAttribute e OutAttribute explicitamente se quiser este comportamento. Sem esses atributos direcionais, o marshaler de interoperabilidade não exporta informações direcionais para a biblioteca de tipos (exporta como In, que é o padrão) e isso pode causar problemas com a marshalling entre apartamentos COM.

  • Se um tipo de referência for passado por referência, ele será processado como Entrada/Saída por defeito.

System.String e System.Text.StringBuilder

Quando os dados são empacotados para código não gerenciado por valor ou por referência, o marshaller normalmente copia os dados para um buffer secundário (possivelmente convertendo conjuntos de caracteres durante a cópia) e passa uma referência ao buffer para o destinatário. A menos que a referência seja alocada com BSTRSysAllocString, a referência é sempre alocada com CoTaskMemAlloc.

Como uma otimização quando um ou StringStringBuilder é empacotado por valor (como uma cadeia de caracteres Unicode), o marshaller passa ao destinatário um ponteiro direto para cadeias de caracteres gerenciadas no buffer Unicode interno em vez de copiá-lo para um novo buffer.

Atenção

Quando uma string é passada por valor, o callee nunca deve alterar a referência passada pelo marshaller. Fazer isso pode corromper a pilha gerenciada.

Quando a System.String é passada por referência, o marshaller copia o conteúdo da string para um buffer secundário antes de fazer a chamada. Em seguida, ele copia o conteúdo do buffer em uma nova cadeia de caracteres no retorno da chamada. Essa técnica garante que a cadeia de caracteres gerenciada imutável permaneça inalterada.

Quando a System.Text.StringBuilder é passado por valor, o marshaller passa uma referência a uma cópia temporária do buffer interno da StringBuilder para o chamador. O chamador e o destinatário devem concordar com o tamanho do buffer. O chamador é responsável por criar um StringBuilder de comprimento adequado. O destinatário deve tomar as precauções necessárias para assegurar que a memória intermédia não é ultrapassada. StringBuilder é uma exceção à regra de que, por defeito, os tipos de referência passados por valor são passados como parâmetros In. StringBuilder é sempre passado como In/Out.

Ver também