Copiar y fijar

Al serializar datos, el serializador de interoperabilidad puede copiar o anclar los datos que se van a serializar. Copiar los datos coloca una copia de datos desde una ubicación de memoria en otra. En la siguiente ilustración se muestran las diferencias entre copiar un tipo de valor y copiar un tipo pasado por referencia de memoria administrada a memoria no administrada.

Diagram that shows how value and reference types are copied.

Los argumentos de método pasados por valor se serializan en código no administrado como valores de la pila. El proceso de copia es directo. Los argumentos que se pasan por referencia se pasan como punteros en la pila. Los tipos de referencia también se pasan por valor y por referencia. Como se muestra en la siguiente ilustración, los tipos de referencia pasados por valor se copian o se anclan:

Diagram showing reference types passed by value and by reference.

Anclar bloquea temporalmente los datos en su ubicación de memoria actual, lo que evita que el recolector de elementos no utilizados de Common Language Runtime los reubique. El serializador ancla los datos para reducir la sobrecarga de la copia y mejorar el rendimiento. El tipo de los datos determina si se copian o se anclan durante el proceso de serialización. El anclaje se realiza de forma automática durante la serialización para objetos como String, pero también se puede anclar manualmente la memoria mediante la clase GCHandle.

Clases que pueden transferirse en bloque de bits con formato

Las clases que pueden transferirse en bloque de bits con formato tienen una distribución (con formato) y representación común de datos fijas tanto en la memoria administrada como no administrada. Cuando estos tipos requieren serialización, un puntero al objeto en el montón se pasa directamente al destinatario. El destinatario de la llamada puede cambiar el contenido de la ubicación de memoria a la que hace referencia el puntero.

Nota

El destinatario puede cambiar el contenido de la memoria si el parámetro está marcado como Out o In/Out. En cambio, el destinatario debe evitar cambiar el contenido cuando el parámetro se establece para serializarse como In, que es el valor predeterminado para tipos que pueden transferirse en bloque de bits. Modificar un objeto In genera problemas cuando se exporta la misma clase a una biblioteca de tipos y se usa para realizar llamadas entre contenedores.

Clases que no pueden transferirse en bloque de bits con formato

Las clases que no pueden transferirse en bloque de bits con formato tienen una distribución fija (con formato) pero la representación de datos es diferente en la memoria administrada y la no administrada. Los datos pueden requerir transformación en las siguientes condiciones:

  • Si una clase que no puede transferirse en bloque de bits se serializa por valor, el destinatario recibe un puntero a una copia de la estructura de datos.

  • Si una clase que no puede transferirse en bloque de bits se serializa por referencia, el destinatario recibe un puntero a un puntero a una copia de la estructura de datos.

  • Si se establece el atributo InAttribute, esta copia siempre se inicializa con el estado de la instancia, y se serializa según sea necesario.

  • Si se establece el atributo OutAttribute, el estado siempre se copia de vuelta en la instancia, y se serializa según sea necesario.

  • Si se establecen InAttribute y OutAttribute, se requieren ambas copias. Si se omite uno de los atributos, el serializador puede optimizar mediante la eliminación de cualquiera de las copias.

Tipos de referencia

Los tipos de referencia se pueden pasar por valor o por referencia. Cuando se pasan por valor, se pasa un puntero al tipo en la pila. Cuando se pasan por referencia, se pasa un puntero a un puntero al tipo en la pila.

Los tipos de referencia tienen el siguiente comportamiento condicional:

  • Si un tipo de referencia se pasa por valor y tiene miembros de tipos que no pueden transferirse en bloque de bits, los tipos se convierten dos veces:

    • Cuando se pasa un argumento al lado no administrado.

    • En la devolución de la llamada.

    Para evitar copias y conversiones innecesarias, estos tipos se serializan como parámetros In. Debe aplicar explícitamente los atributos InAttribute y OutAttribute a un argumento para que el autor de la llamada vea los cambios realizados por el destinatario.

  • Si un tipo de referencia se pasa por valor y solo tiene miembros de tipos que pueden transferirse en bloque de bits, se puede anclar durante la serialización y el autor de la llamada verá cualquier cambio realizado en los miembros del tipo por el destinatario. Aplique InAttribute y OutAttribute explícitamente si quiere este comportamiento. Sin estos atributos direccionales, el serializador de interoperabilidad no exporta información direccional a la biblioteca de tipos (se exporta como In, que es el valor predeterminado) y esto puede causar problemas con la serialización COM entre contenedores.

  • Si un tipo de referencia se pasa por referencia, se serializará como In/Out de forma predeterminada.

System.String y System.Text.StringBuilder

Cuando los datos se serializan en código no administrado por valor o por referencia, el serializador normalmente copia los datos en un búfer secundario (y posiblemente convierte los juegos de caracteres durante la copia) y pasa una referencia al búfer al destinatario. A menos que la referencia sea un BSTR asignado con SysAllocString, la referencia siempre se asigna con CoTaskMemAlloc.

Como una optimización cuando se serializa por valor String o StringBuilder (por ejemplo, una cadena de caracteres Unicode), el serializador pasa al destinatario un puntero directo a las cadenas administradas en el búfer interno de Unicode en lugar de copiarlo en un búfer nuevo.

Precaución

Cuando se pasa una cadena por valor, el destinatario nunca debe modificar la referencia que pasa el serializador. Esto puede dañar el montón administrado.

Cuando se pasa System.String por referencia, el serializador copia el contenido de la cadena en un búfer secundario antes de realizar la llamada. Después, copia el contenido del búfer en una nueva cadena en la devolución de la llamada. Esta técnica garantiza que la cadena administrada inmutable permanece sin modificar.

Cuando se pasa System.Text.StringBuilder por valor, el serializador pasa al autor de la llamada una referencia a una copia temporal del búfer interno de StringBuilder. El autor de la llamada y el destinatario deben acordar el tamaño del búfer. El autor de la llamada es responsable de crear un StringBuilder de la longitud adecuada. El destinatario debe tomar las precauciones necesarias para asegurarse de que el búfer no se desborda. StringBuilder es una excepción a la regla de que los tipos de referencia que se pasan por valor se pasan como parámetros In de forma predeterminada. StringBuilder siempre se pasa como In/Out.

Consulte también