Cálculo de referencias predeterminado para objetos
Actualización: noviembre 2007
Los parámetros y campos cuyo tipo es System.Object pueden exponerse a código no administrado como uno de los tipos siguientes:
Una variante cuando el objeto es un parámetro.
Una interfaz cuando el objeto es un campo de una estructura.
Sólo la interoperatividad COM admite el cálculo de referencias para tipos de objetos. De forma predeterminada, se calcularán las referencias de objetos para variantes COM. Estas reglas sólo se aplican al tipo Object y no son válidas para objetos con establecimiento inflexible de tipos que se derivan de la clase Object.
Este tema ofrece la siguiente información adicional sobre el cálculo de referencias de tipos de objetos:
Calcular referencias de opciones
Calcular referencias de objetos para interfaces
Calcular referencias de objetos para variantes
Calcular referencias de variantes para objetos
Calcular referencias de variantes ByRef
Calcular referencias de opciones
En la tabla siguiente se muestran las opciones de cálculo de referencias para el tipo de datos Object. El atributo MarshalAsAttribute proporciona varios valores de la enumeración UnmanagedType para calcular las referencias de objetos.
Tipo de enumeración |
Descripción de formato no administrado |
---|---|
UnmanagedType.Struct (valor predeterminado para los parámetros) |
Variante del estilo de COM. |
UnmanagedType.Interface |
Interfaz IDispatch, si es posible; si no, interfaz IUnknown. |
UnmanagedType.IUnknown (valor predeterminado para los campos) |
Interfaz IUnknown. |
UnmanagedType.IDispatch |
Una interfaz IDispatch. |
En el ejemplo siguiente se muestra la definición de interfaz administrada para MarshalObject.
Interface MarshalObject
Sub SetVariant(o As Object)
Sub SetVariantRef(ByRef o As Object)
Function GetVariant() As Object
Sub SetIDispatch( <MarshalAs(UnmanagedType.IDispatch)> o As Object)
Sub SetIDispatchRef(ByRef <MarshalAs(UnmanagedType.IDispatch)> o _
As Object)
Function GetIDispatch() As <MarshalAs(UnmanagedType.IDispatch)> Object
Sub SetIUnknown( <MarshalAs(UnmanagedType.IUnknown)> o As Object)
Sub SetIUnknownRef(ByRef <MarshalAs(UnmanagedType.IUnknown)> o _
As Object)
Function GetIUnknown() As <MarshalAs(UnmanagedType.IUnknown)> Object
End Interface
interface MarshalObject {
void SetVariant(Object o);
void SetVariantRef(ref Object o);
Object GetVariant();
void SetIDispatch ([MarshalAs(UnmanagedType.IDispatch)]Object o);
void SetIDispatchRef([MarshalAs(UnmanagedType.IDispatch)]ref Object o);
[MarshalAs(UnmanagedType.IDispatch)] Object GetIDispatch();
void SetIUnknown ([MarshalAs(UnmanagedType.IUnknown)]Object o);
void SetIUnknownRef([MarshalAs(UnmanagedType.IUnknown)]ref Object o);
[MarshalAs(UnmanagedType.IUnknown)] Object GetIUnknown();
}
En el código siguiente se exporta la interfaz MarshalObject a una biblioteca de tipos.
interface MarshalObject {
HRESULT SetVariant([in] VARIANT o);
HRESULT SetVariantRef([in,out] VARIANT *o);
HRESULT GetVariant([out,retval] VARIANT *o)
HRESULT SetIDispatch([in] IDispatch *o);
HRESULT SetIDispatchRef([in,out] IDispatch **o);
HRESULT GetIDispatch([out,retval] IDispatch **o)
HRESULT SetIUnknown([in] IUnknown *o);
HRESULT SetIUnknownRef([in,out] IUnknown **o);
HRESULT GetIUnknown([out,retval] IUnknown **o)
}
Nota: |
---|
El contador de referencias de interoperabilidad libera automáticamente cualquier objeto asignado dentro de la variante tras la llamada. |
En el ejemplo siguiente se muestra un tipo de valor con formato.
Public Structure ObjectHolder
Dim o1 As Object
<MarshalAs(UnmanagedType.IDispatch)> Public o2 As Object
End Structure
public struct ObjectHolder {
Object o1;
[MarshalAs(UnmanagedType.IDispatch)]public Object o2;
}
En el código siguiente se exporta el tipo con formato a una biblioteca de tipos.
struct ObjectHolder {
VARIANT o1;
IDispatch *o2;
}
Calcular referencias de objetos para interfaces
Cuando un objeto se expone en COM como una interfaz, ésta es la interfaz de clase para el tipo administrado Object (la interfaz _Object). Esta interfaz tiene el tipoIDispatch (UnmanagedType.IDispatch) o IUnknown (UnmanagedType.IUnknown) en la biblioteca de tipos resultante. Los clientes COM pueden invocar dinámicamente a los miembros de la clase administrada o a cualquier miembro implementado mediante sus clases derivadas a través de la interfaz _Object. Asimismo, el cliente puede llamar a QueryInterface para obtener cualquier otra interfaz que implemente explícitamente el tipo administrado.
Calcular referencias de objetos para variantes
Cuando se calculan referencias de un objeto para una variante, el tipo de variante interna se determina en tiempo de ejecución, en función de las reglas siguientes:
Si la referencia de objeto es null (Nothing en Visual Basic), se calculan las referencias del objeto para una variante de tipo VT_EMPTY.
Si el objeto es una instancia de cualquier tipo que aparezca en la tabla siguiente, el tipo de variante resultante se determina mediante las reglas integradas en el contador de referencias, enumeradas en la tabla.
Otros objetos que necesitan controlar explícitamente el comportamiento del cálculo de referencias pueden implementar la interfaz IConvertible. En ese caso, el tipo de variante se determina mediante el tipo de código que devuelve el método IConvertible.GetTypeCode. De lo contrario, se calcularán las referencias del objeto como variante de tipo VT_UNKNOWN.
Calcular referencias de tipos de sistema para variantes
En la tabla siguiente se muestran tipos de objeto administrado y sus tipos de variante COM correspondientes. Estos tipos sólo se convierten cuando la firma del método que se llama es de tipo System.Object.
Tipo de objeto |
Tipo de variante COM |
---|---|
Referencia de objeto null (Nothing en Visual Basic). |
VT_EMPTY |
VT_NULL |
|
VT_ERROR |
|
VT_ERROR con E_PARAMNOTFOUND |
|
VT_DISPATCH |
|
VT_UNKNOWN |
|
VT_CY |
|
VT_BOOL |
|
VT_I1 |
|
VT_UI1 |
|
VT_I2 |
|
VT_UI2 |
|
VT_I4 |
|
VT_UI4 |
|
VT_I8 |
|
VT_UI8 |
|
VT_R4 |
|
VT_R8 |
|
VT_DECIMAL |
|
VT_DATE |
|
VT_BSTR |
|
VT_INT |
|
VT_UINT |
|
VT_ARRAY |
Utilizando la interfaz MarshalObject definida en el ejemplo anterior, en el ejemplo de código siguiente se muestra cómo pasar varios tipos de variantes a un servidor COM.
Dim mo As New MarshalObject()
mo.SetVariant(Nothing) ' Marshal as variant of type VT_EMPTY.
mo.SetVariant(System.DBNull.Value) ' Marshal as variant of type VT_NULL.
mo.SetVariant(CInt(27)) ' Marshal as variant of type VT_I2.
mo.SetVariant(CLng(27)) ' Marshal as variant of type VT_I4.
mo.SetVariant(CSng(27.0)) ' Marshal as variant of type VT_R4.
mo.SetVariant(CDbl(27.0)) ' Marshal as variant of type VT_R8.
MarshalObject mo = new MarshalObject();
mo.SetVariant(null); // Marshal as variant of type VT_EMPTY.
mo.SetVariant(System.DBNull.Value); // Marshal as variant of type VT_NULL.
mo.SetVariant((int)27); // Marshal as variant of type VT_I2.
mo.SetVariant((long)27); // Marshal as variant of type VT_I4.
mo.SetVariant((single)27.0); // Marshal as variant of type VT_R4.
mo.SetVariant((double)27.0); // Marshal as variant of type VT_R8.
En los tipos COM que no tienen tipos administrados correspondientes se pueden calcular las referencias usando clases de contenedor como ErrorWrapper, DispatchWrapper, UnknownWrapper y CurrencyWrapper. En el ejemplo de código siguiente se muestra cómo utilizar estos contenedores para pasar varios tipos de variantes a un servidor COM.
Imports System.Runtime.InteropServices
' Pass inew as a variant of type VT_UNKNOWN interface.
mo.SetVariant(New UnknownWrapper(inew))
' Pass inew as a variant of type VT_DISPATCH interface.
mo.SetVariant(New DispatchWrapper(inew))
' Pass a value as a variant of type VT_ERROR interface.
mo.SetVariant(New ErrorWrapper(&H80054002))
' Pass a value as a variant of type VT_CURRENCY interface.
mo.SetVariant(New CurrencyWrapper(New Decimal(5.25)))
using System.Runtime.InteropServices;
// Pass inew as a variant of type VT_UNKNOWN interface.
mo.SetVariant(new UnknownWrapper(inew));
// Pass inew as a variant of type VT_DISPATCH interface.
mo.SetVariant(new DispatchWrapper(inew));
// Pass a value as a variant of type VT_ERROR interface.
mo.SetVariant(new ErrorWrapper(0x80054002));
// Pass a value as a variant of type VT_CURRENCY interface.
mo.SetVariant(new CurrencyWrapper(new Decimal(5.25)));
Las clases contenedoras se definen en el espacio de nombres System.Runtime.InteropServices.
Calcular referencias de la interfaz IConvertible para variantes
Los tipos que no aparecían en la sección anterior pueden controlar cómo se calculan sus referencias mediante la implementación de la interfaz IConvertible. Si el objeto implementa la interfaz IConvertible, el tipo de variante COM se determina en tiempo de ejecución mediante el valor de la enumeración TypeCode que devuelve el método IConvertible.GetTypeCode.
En la tabla siguiente se muestran los posibles valores para la enumeración TypeCode y el tipo de variante COM correspondiente para cada valor.
TypeCode |
Tipo de variante COM |
---|---|
TypeCode.Empty |
VT_EMPTY |
TypeCode.Object |
VT_UNKNOWN |
TypeCode.DBNull |
VT_NULL |
TypeCode.Boolean |
VT_BOOL |
TypeCode.Char |
VT_UI2 |
TypeCode.Sbyte |
VT_I1 |
TypeCode.Byte |
VT_UI1 |
TypeCode.Int16 |
VT_I2 |
TypeCode.UInt16 |
VT_UI2 |
TypeCode.Int32 |
VT_I4 |
TypeCode.UInt32 |
VT_UI4 |
TypeCode.Int64 |
VT_I8 |
TypeCode.UInt64 |
VT_UI8 |
TypeCode.Single |
VT_R4 |
TypeCode.Double |
VT_R8 |
TypeCode.Decimal |
VT_DECIMAL |
TypeCode.DateTime |
VT_DATE |
TypeCode.String |
VT_BSTR |
No se admite. |
VT_INT |
No se admite. |
VT_UINT |
No se admite. |
VT_ARRAY |
No se admite. |
VT_RECORD |
No se admite. |
VT_CY |
No se admite. |
VT_VARIANT |
El valor de la variante COM se determina llamando a la interfaz IConvertible.ToType, donde ToType es la rutina de conversión que corresponde al tipo devuelto por IConvertible.GetTypeCode. Por ejemplo, las referencias a un objeto que devuelve TypeCode.Double de IConvertible.GetTypeCode se calculan como una variante COM de tipo VT_R8. Puede obtener el valor de la variante (almacenado en el campo dblVal de la variante COM) si lo convierte a la interfaz IConvertible y llama al método ToDouble.
Calcular referencias de variantes para objetos
Al calcular referencias de una variante para un objeto, el tipo y, en ocasiones, el valor de la variante cuyas referencias se calculan determinan el tipo de objeto producido. En la tabla siguiente se identifica cada tipo de variante y el tipo de objeto correspondiente que crea el contador de referencias cuando se pasa una variante COM a .NET Framework.
Tipo de variante COM |
Tipo de objeto |
---|---|
VT_EMPTY |
Referencia de objeto null (Nothing en Visual Basic). |
VT_NULL |
|
VT_DISPATCH |
System.__ComObject o null si (pdispVal == null) |
VT_UNKNOWN |
System.__ComObject o null si (pdispVal == null) |
VT_ERROR |
|
VT_BOOL |
|
VT_I1 |
|
VT_UI1 |
|
VT_I2 |
|
VT_UI2 |
|
VT_I4 |
|
VT_UI4 |
|
VT_I8 |
|
VT_UI8 |
|
VT_R4 |
|
VT_R8 |
|
VT_DECIMAL |
|
VT_DATE |
|
VT_BSTR |
|
VT_INT |
|
VT_UINT |
|
VT_ARRAY | VT_* |
|
VT_CY |
|
VT_RECORD |
Tipo de valor correspondiente al que se le ha aplicado la conversión boxing. |
VT_VARIANT |
No se admite. |
Es posible que los tipos de variante que se pasan de COM a código administrado y de nuevo a COM no se mantengan sin cambios durante toda la llamada. Considere lo que sucede cuando una variante de tipo VT_DISPATCH se pasa de COM a .NET Framework. Durante el cálculo de referencias, la variante se convierte a System.Object. Si Object se pasa de nuevo a COM, se calculan de nuevo sus referencias para una variante de tipo VT_UNKNOWN. No hay ninguna garantía de que la variante producida cuando se calculan referencias de un objeto a partir de código administrado para COM sea del mismo tipo que la variante utilizada inicialmente para producir el objeto.
Calcular referencias de variantes ByRef
Aunque las variantes se pueden pasar por valor o por referencia, el marcador VT_BYREF también se puede utilizar con cualquier tipo de variante para indicar que su contenido se está pasando por referencia en vez de por valor. La diferencia entre calcular referencias de variantes por referencia y con el marcador VT_BYREF establecido puede resultar confuso. En la ilustración siguiente se aclaran las diferencias.
Variantes pasadas por valor y por referencia
Comportamiento predeterminado para calcular referencias de objetos y variantes por valor
Al pasar objetos de código administrado a COM, el contenido del objeto se copia en una nueva variante que crea el contador de referencias usando las reglas definidas en Calcular referencias de objetos para variantes. Los cambios realizados en la variante en el lado no administrado no se vuelven a propagar al objeto original al volver de la llamada.
Al pasar variantes de COM a código administrado, el contenido de la variante se copia en un objeto recién creado utilizando las reglas definidas en Calcular referencias de variantes para objetos. Los cambios realizados en el objeto en el lado administrado no se vuelven a propagar a la variante original al volver de la llamada.
Comportamiento predeterminado para calcular referencias de objetos y variantes por referencia
Para propagar los cambios de nuevo al llamador, los parámetros deben pasarse por referencia. Por ejemplo, puede utilizar la palabra clave ref en C# (o ByRef en el código administrado de Visual Basic) para pasar parámetros por referencia. En COM, para pasar los parámetros por referencia se usa un puntero como una variant *.
Al pasar un objeto a COM por referencia, el contador de referencias crea una nueva variante y copia el contenido de la referencia de objeto a la variante antes de realizar la llamada. La variante se pasa a la función no administrada, donde el usuario puede cambiar su contenido. Los cambios realizados en la variante en el lado no administrado se vuelven a propagar al objeto original al volver de la llamada. Si el tipo de la variante difiere del tipo de la variante pasada a la llamada, los cambios se vuelven a propagar a un objeto de un tipo distinto. Es decir, el tipo de objeto pasado en la llamada puede diferir del tipo de objeto que devuelve la llamada.
Al pasar una variante a código administrado por referencia, el contador de referencias crea un objeto nuevo y copia el contenido de la variante en él antes de realizarse la llamada. Una referencia al objeto se pasa a la función administrada, donde el usuario puede cambiar el objeto. Los cambios realizados en el objeto al que se hace referencia se vuelven a propagar a la variante original al volver de la llamada. Si el tipo del objeto difiere del tipo de objeto pasado en la llamada, el tipo de la variante original se cambia y el valor se propaga de nuevo en la variante. De nuevo, el tipo de la variante pasado en la llamada puede diferir del tipo de variante que devuelve la llamada.
Comportamiento predeterminado para calcular referencias de una variante con el marcador VT_BYREF establecido
Una variante que se pasa a código administrado por valor puede tener el marcador VT_BYREF establecido para indicar que contiene una referencia en lugar de un valor. En este caso, se siguen calculando las referencias de la variante para un objeto porque la variante se está pasando por valor. El contador de referencias anula automáticamente las referencias del contenido de la variante y la copia en un objeto recién creado antes de realizar la llamada. Seguidamente, el objeto se pasa a la función administrada; no obstante, al volver de la llamada, el objeto no se vuelve a propagar a la variante original. Los cambios realizados en el objeto administrado se pierden.
Precaución: No es posible cambiar el valor de una variante pasada por valor, aunque la variante tenga el marcador VT_BYREF establecido.
Una variante que se pasa a código administrado por referencia también puede tener el marcador VT_BYREF establecido para indicar que la variante contiene otra referencia. Si es así, se calcula la referencia de la la variante a un objeto ref, ya que la variante se pasa por referencia. El contador de referencias anula automáticamente las referencias del contenido de la variante y la copia en un objeto recién creado antes de realizar la llamada. Al volver de la llamada, el valor del objeto se vuelve a propagar a la referencia dentro de la variante original sólo si el objeto es del mismo tipo que el objeto que se pasa. Es decir, la propagación no cambia el tipo de una variante con el marcador VT_BYREF establecido. Si el tipo del objeto se cambia durante la llamada, se inicia una excepción InvalidCastException al volver de la llamada.
En la tabla siguiente se resumen las reglas de propagación para variantes y objetos.
De |
A |
Cambios que se vuelven a propagar |
---|---|---|
Variant v |
Object o |
Nunca |
Object o |
Variant v |
Nunca |
Variant *pv |
Ref Object o |
Siempre |
Ref object o |
Variant *pv |
Siempre |
Variant v(VT_BYREF|VT_*) |
Object o |
Nunca |
Variant v(VT_BYREF|VT_) |
Ref Object o |
Sólo si el tipo no ha cambiado. |
Vea también
Conceptos
Tipos que pueden o que no pueden representarse como bits o bytes