Cálculo de referencias predeterminado para tipos de valor
Actualización: noviembre 2007
La mayor parte de los tipos de valor, como enteros y números de punto flotante, pueden representarse como bits o bytes y no requieren cálculo de referencias. Otros tipos que no pueden representarse como bits o bytes tienen representaciones distintas en memoria administrada y en memoria no administrada, y no requieren cálculo de referencias. Sin embargo, otros tipos requieren un formato explícito en el límite de la interoperación.
En este tema se ofrece la información siguiente sobre los tipos de valor con formato:
Tipos de valor utilizados en invocación de plataforma
Tipos de valor utilizados en la interoperatividad COM
Además de describir tipos con formato, en este tema se identifican los Tipos de valor System que presentan un comportamiento inusual en el cálculo de referencias.
Un tipo con formato es un tipo complejo que contiene información que controla explícitamente la colocación de sus miembros en la memoria. La información de la colocación de los miembros se proporciona mediante el atributo StructLayoutAttribute. La colocación puede establecerse en uno de los siguientes valores de la enumeración LayoutKind:
LayoutKind.Automatic
Indica que Common Language Runtime puede volver a ordenar los miembros del tipo para lograr una mayor eficacia. No obstante, cuando se pasa un tipo de valor a código no administrado, la colocación de los miembros es predecible. Un intento de calcular referencias de esa estructura provoca automáticamente una excepción.
LayoutKind.Sequential
Indica que los miembros del tipo se dispondrán en la memoria no administrada en el orden en que aparecen en la definición de tipo administrado.
LayoutKind.Explicit
Indica que los miembros se colocan en función del atributo FieldOffsetAttribute proporcionado con cada campo.
Tipos de valor utilizados en invocación de plataforma
En el ejemplo siguiente los tipos Point y Rect proporcionan información de la colocación de los miembros utilizando el atributo StructLayoutAttribute.
Imports System.Runtime.InteropServices
<StructLayout(LayoutKind.Sequential)> Public Structure Point
Public x As Integer
Public y As Integer
End Structure
<StructLayout(LayoutKind.Explicit)> Public Structure Rect
<FieldOffset(0)> Public left As Integer
<FieldOffset(4)> Public top As Integer
<FieldOffset(8)> Public right As Integer
<FieldOffset(12)> Public bottom As Integer
End Structure
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Sequential)]
public struct Point {
public int x;
public int y;
}
[StructLayout(LayoutKind.Explicit)]
public struct Rect {
[FieldOffset(0)] public int left;
[FieldOffset(4)] public int top;
[FieldOffset(8)] public int right;
[FieldOffset(12)] public int bottom;
}
Cuando se calculan las referencias para código no administrado, las referencias de estos tipos con formato se calculan como estructuras del estilo de C. Esto ofrece un modo sencillo de llamar a una API no administrada que tiene argumentos de estructura. Por ejemplo, las estructuras POINT y RECT se pueden pasar a la función PtInRect de la API Win32 de Microsoft de este modo:
BOOL PtInRect(const RECT *lprc, POINT pt);
Puede pasar estructuras utilizando la siguiente definición de invocación de plataforma:
Class Win32API
Declare Auto Function PtInRect Lib "User32.dll" _
(ByRef r As Rect, p As Point) As Boolean
End Class
class Win32API {
[DllImport("User32.dll")]
public static extern Bool PtInRect(ref Rect r, Point p);
}
El tipo de valor Rect se debe pasar por referencia porque la API no administrada está esperando que se pase un puntero a RECT para la función. El tipo de valor Point se pasa por valor porque la API no administrada espera que se pase POINT en la pila. Esta diferencia sutil es muy importante. Las referencias se pasan a código no administrado como punteros. Los valores se pasan a código no administrado en la pila.
Nota: |
---|
Cuando se calculan referencias de un tipo con formato como una estructura, sólo se puede obtener acceso a los campos de ese tipo. Si el tipo tiene métodos, propiedades o eventos, son inaccesibles desde el código no administrado. |
Las referencias de clases también se pueden calcular para código no administrado como estructuras del estilo de C, siempre que la colocación de los miembros sea fija. La información de la colocación de una clase se proporciona también con el atributo StructLayoutAttribute. La diferencia principal entre los tipos de valor con colocación fija y las clases con colocación fija es el modo en que se calculan sus referencias para código no administrado. Los tipos de valor se pasan por valor (en la pila) y, por lo tanto, cualquier cambio que el destinatario de la llamada realiza en los miembros no lo ve el llamador. Los tipos de referencia se pasan por referencia (se pasa una referencia del tipo en la pila); como consecuencia, el llamador ve todos los cambios que el destinatario de la llamada efectúa en los miembros de tipos que pueden representarse como bits o bytes.
Nota: |
---|
Si un tipo de referencia tiene miembros de tipos que no pueden representarse como bits o bytes, se requiere una conversión doble: la primera vez cuando se pasa un argumento al lado no administrado y la segunda al volver de la llamada. Debido a esta sobrecarga adicional, los parámetros In/Out deben aplicarse explícitamente si el llamador desea ver los cambios que realiza el destinatario de la llamada. |
En el ejemplo siguiente, en la clase SystemTime los miembros están colocados de forma secuencial y puede pasarse a la función GetSystemTime de la API Win32.
<StructLayout(LayoutKind.Sequential)> Public Class SystemTime
Public wYear As System.UInt16
Public wMonth As System.UInt16
Public wDayOfWeek As System.UInt16
Public wDay As System.UInt16
Public wHour As System.UInt16
Public wMinute As System.UInt16
Public wSecond As System.UInt16
Public wMilliseconds As System.UInt16
End Class
[StructLayout(LayoutKind.Sequential)]
public class SystemTime {
public ushort wYear;
public ushort wMonth;
public ushort wDayOfWeek;
public ushort wDay;
public ushort wHour;
public ushort wMinute;
public ushort wSecond;
public ushort wMilliseconds;
}
La función GetSystemTime se define de este modo:
void GetSystemTime(SYSTEMTIME* SystemTime);
La definición de invocación de plataforma equivalente para GetSystemTime es la siguiente:
Public Class Win32
Declare Auto Sub GetSystemTime Lib "Kernel32.dll" (ByVal sysTime _
As SystemTime)
End Class
class Win32API {
[DllImport("Kernel32.dll", CharSet=CharSet.Auto)]
public static extern void GetSystemTime(SystemTime st);
}
Observe que el argumento SystemTime no se escribe como un argumento de referencia porque es una clase, no un tipo de valor. A diferencia de los tipos de valor, las clases siempre se pasan por referencia.
En el ejemplo de código siguiente se muestra una clase Point distinta que tiene un método llamado SetXY. Como el tipo tiene una colocación secuencial, puede pasarse a código no administrado y sus referencias se pueden calcular como una estructura. No obstante, el miembro SetXY no es invocable desde código no administrado, aunque el objeto se pase por referencia.
<StructLayout(LayoutKind.Sequential)> Public Class Point
Private x, y As Integer
Public Sub SetXY(x As Integer, y As Integer)
Me.x = x
Me.y = y
End Sub
End Class
[StructLayout(LayoutKind.Sequential)]
public class Point {
int x, y;
public void SetXY(int x, int y){
this.x = x;
this.y = y;
}
}
Tipos de valor utilizados en la interoperatividad COM
Los tipos con formato también se pueden pasar a llamadas de método de interoperatividad COM. De hecho, cuando se exportan a una biblioteca de tipos, los tipos de valor se convierten automáticamente en estructuras. Como se muestra en el ejemplo siguiente, el tipo de valor Point se convierte en una definición de tipo (typedef) con el nombre Point. Todas las referencias al tipo de valor Point de otra ubicación en la biblioteca de tipos se reemplazarán con el typedef Point.
Representación de biblioteca de tipos
typedef struct tagPoint {
int x;
int y;
} Point;
interface _Graphics {
…
HRESULT SetPoint ([in] Point p)
HRESULT SetPointRef ([in,out] Point *p)
HRESULT GetPoint ([out,retval] Point *p)
}
Las mismas reglas utilizadas para calcular referencias de valores y referencias a llamadas de invocación de plataforma se utilizan al calcular referencias mediante interfaces COM. Por ejemplo, cuando se pasa una instancia del tipo de valor Point desde .NET Framework a COM, Point se pasa por valor. Si el tipo de valor Point se pasa por referencia, un puntero a Point se pasa en la pila. El contador de referencias interoperativo no admite niveles superiores de direccionamiento indirecto (Point **) en ningún sentido.
Nota: |
---|
Las estructuras que tienen el valor de la enumeración LayoutKind establecido en Explicit no se pueden utilizar en interoperabilidad COM porque la biblioteca de tipos exportada no puede expresar una colocación explícita. |
Tipos de valor System
El espacio de nombres System tiene varios tipos de valor que representan el formato al que se aplica la conversión boxing de los tipos primitivos del motor en tiempo de ejecución. Por ejemplo, la estructura del tipo de valor System.Int32 representa el formato al que se aplica la conversión boxing de ELEMENT_TYPE_I4. En lugar de calcular las referencias de estos tipos como estructuras, como en otros tipos con formato, sus referencias se calculan del mismo modo que los tipos primitivos a los que se aplica la conversión boxing. Por tanto, se calculan las referencias de System.Int32 como ELEMENT_TYPE_I4, en lugar de como una estructura que contiene un miembro único de tipo long. La tabla siguiente contiene una lista de los tipos de valor del espacio de nombres System que son representaciones a las que se aplica la conversión boxing de tipos primitivos.
Tipo de valor System |
Tipo de elemento |
---|---|
ELEMENT_TYPE_BOOLEAN |
|
ELEMENT_TYPE_I1 |
|
ELEMENT_TYPE_UI1 |
|
ELEMENT_TYPE_CHAR |
|
ELEMENT_TYPE_I2 |
|
ELEMENT_TYPE_U2 |
|
ELEMENT_TYPE_I4 |
|
ELEMENT_TYPE_U4 |
|
ELEMENT_TYPE_I8 |
|
ELEMENT_TYPE_U8 |
|
ELEMENT_TYPE_R4 |
|
ELEMENT_TYPE_R8 |
|
ELEMENT_TYPE_STRING |
|
ELEMENT_TYPE_I |
|
ELEMENT_TYPE_U |
Otros tipos de valor del espacio de nombres System se controlan de forma distinta. Dado que el código no administrado ya tiene formatos establecidos para estos tipos, el contador de referencias dispone de reglas especiales para calcular sus referencias. En la tabla siguiente se enumeran los tipos de valor especiales del espacio de nombres System, así como el tipo administrado para el que se calculan las referencias.
Tipo de valor System |
Tipo IDL |
---|---|
DATE |
|
DECIMAL |
|
GUID |
|
OLE_COLOR |
En el código siguiente se muestra la definición de los tipos no administrados DATE, GUID, DECIMAL y OLE_COLOR en la biblioteca de tipos Stdole2.
Representación de biblioteca de tipos
typedef double DATE;
typedef DWORD OLE_COLOR;
typedef struct tagDEC {
USHORT wReserved;
BYTE scale;
BYTE sign;
ULONG Hi32;
ULONGLONG Lo64;
} DECIMAL;
typedef struct tagGUID {
DWORD Data1;
WORD Data2;
WORD Data3;
BYTE Data4[ 8 ];
} GUID;
En el código siguiente se muestran las definiciones correspondientes en la interfaz administrada IValueTypes.
Public Interface IValueTypes
Sub M1(d As System.DateTime)
Sub M2(d As System.Guid)
Sub M3(d As System.Decimal)
Sub M4(d As System.Drawing.Color)
End Interface
public interface IValueTypes {
void M1(System.DateTime d);
void M2(System.Guid d);
void M3(System.Decimal d);
void M4(System.Drawing.Color d);
}
Representación de biblioteca de tipos
[…]
interface IValueTypes : IDispatch {
HRESULT M1([in] DATE d);
HRESULT M2([in] GUID d);
HRESULT M3([in] DECIMAL d);
HRESULT M4([in] OLE_COLOR d);
};
Vea también
Conceptos
Tipos que pueden o que no pueden representarse como bits o bytes