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.

Diagram that shows how value and reference types are copied.

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:

Diagram showing reference types passed by value and by reference.

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 alterar o conteúdo do local da memória que está sendo referenciado pelo ponteiro.

Nota

O destinatário pode alterar o conteúdo da memória se o parâmetro estiver marcado como out ou In/out. Por outro lado, o destinatário deve evitar alterar o conteúdo quando o parâmetro é definido como 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 InAttribute e OutAttribute estiverem definidos, ambas as cópias serã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 é passado por valor e tem membros de tipos não blittable, 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. Você deve aplicar explicitamente os atributos InAttribute e OutAttribute a um argumento para que o chamador veja as alterações feitas pelo destinatário.

  • 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 desejar esse comportamento. Sem esses atributos direcionais, o marshaller 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 o empacotamento de apartamento cruzado COM.

  • Se um tipo de referência for passado por referência, ele será empacotado como Entrada/Saída por padrão.

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 um BSTR alocado com SysAllocString, 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 um System.Text.StringBuilder é passado por valor, o marshaller passa uma referência a uma cópia temporária do buffer interno do 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 os tipos de referência passados pelo valor são passados como In parâmetros por padrão. StringBuilder é sempre passado como In/Out.

Consulte também