Pregunta ¿Cómo sabe el tiempo de ejecución el tipo exacto de un tipo de valor en caja?


Entiendo lo que es el boxeo. Un tipo de valor está encuadrado en un objeto / tipo de referencia y luego se almacena en el montón administrado como un objeto. Pero no puedo salir del boxeo.

Unboxing convierte su tipo de objeto / referencia de nuevo al tipo de valor

int i = 123;          // A value type
object box = i;       // Boxing
int j = (int)box;     // Unboxing

Bien. Pero si trato de descomponer un tipo de valor en otro tipo de valor, por ejemplo, durante mucho tiempo en el ejemplo anterior, arroja InvalidCastException

long d = (long)box;

Me deja con una idea que puede ser que el tiempo de ejecución sepa implícitamente el TIPO real de tipo de valor dentro del objeto "caja". Si tengo razón, me pregunto dónde se almacena esta información de tipo.

EDITAR:

Ya que int es implícitamente convertible a long. Esto es lo que me confunde.

int i = 123;
long lng = i;

está perfectamente bien porque no tiene boxeo / desempaquetado involucrado.


5
2017-07-30 11:11


origen


Respuestas:


Cuando un valor está encuadrado, obtiene un encabezado de objeto. El tipo que tiene cualquier tipo que deriva de System.Object. El valor sigue ese encabezado. El encabezado contiene dos campos, uno es el "syncblk", tiene varios usos que están fuera del alcance de la pregunta. El segundo campo describe el tipo de objeto.

Es por lo que preguntas. Tiene varios nombres en la literatura, más comúnmente "manejador de tipo" o "puntero de tabla de método". Esta última es la descripción más precisa, es un indicador de la información que CLR realiza un seguimiento cada vez que carga un tipo. Muchas de las características del framework dependen de ello. Object.GetType () por supuesto. Cualquier reparto en su código, así como el es y como los operadores lo usan. Estos moldes son seguros, por lo que no puede convertir a un perro en un gato, el identificador de tipo ofrece esta garantía. El puntero de tabla de método para su caja. En t apunta a la tabla de métodos para System.Int32

El boxeo era muy común en .NET 1.x, antes de que los genéricos estuvieran disponibles. Todos los tipos de colección comunes almacenados objeto en lugar de T. Por lo tanto, colocar un elemento en la colección (implícito) requiere boxeo, volver a sacarlo requiere explícito unboxing con un lanzamiento.

Para hacer esto eficiente, era muy importante que la fluctuación no tuviera que considerar la posibilidad de que conversión sería requerido. Porque eso requiere mucho más trabajo. Por lo tanto, el lenguaje C # incluía la regla de que desempaquetar a otro tipo es ilegal. Todo lo que se necesita ahora es una comprobación en el identificador de tipo para asegurarse de que es el tipo esperado. El jitter compara directamente el puntero de la tabla del método con el de System.Int32 en su caso. Y el valor incorporado en el objeto se puede copiar directamente sin ningún problema de conversión. Bastante rápido, tan rápido como sea posible, todo esto se puede hacer con un código de máquina en línea sin ninguna llamada CLR.

Esta regla es específica de C #, VB.NET no la tiene. Compensación típica entre esos dos idiomas, el enfoque de C # está en la velocidad, VB.NET en la conveniencia. La conversión a otro tipo cuando unboxing no es un problema, todos los tipos de valor simples implementan IConvertible. Lo escribes explícitamente en tu código, usando la clase de ayuda Convert:

        int i = 123;                    // A value type
        object box = i;                 // Boxing
        long j = Convert.ToInt64(box);  // Conversion + unboxing

Lo cual es bastante similar al código que el compilador VB.NET genera automáticamente.


8
2017-07-30 12:24



Es porque la instrucción de boxeo agrega un token de tipo de valor al objeto de resultado MSDN. Cuando se está descargando el valor del objeto, esta variable es de tipo conocido (y tamaño en la memoria). Por lo tanto, debe convertir el objeto al tipo de valor original.

En su ejemplo, incluso no necesita lanzarlo desde int a long, porque es un lanzamiento implícito.


4
2017-07-30 11:25



Es porque cuando haces boxeo en lugar de mover el tipo de valor de la pila al montón, crea una copia de él en el montón y almacena la referencia de él en la pila en un nuevo cuadro de pila. Por lo tanto, su objeto de pila original, es decir, el objeto de tipo valor, junto con su información de tipo de datos, permanece en la pila y mantiene su historial. Ahora, en el momento de desempaquetar, compara el tipo de objeto del montón al tipo de datos original en la pila y si encuentra discrepancia, da el error. Por lo tanto, es necesario usar el mismo tipo de datos que encajó mientras realizaba el desempaquetado.


2
2017-07-30 12:29



Cada objeto de referencia tiene un montón de metadatos asociados con él. Esto incluye el tipo exacto del objeto dado (por lo que puede tener seguridad de tipo).

Así que mientras int es por valor, esta información realmente falta (no es que importe), pero una vez que la encierras, crea un nuevo objeto con todos los metadatos necesarios. Esto también significa que mientras un int Solo tiene 4 bytes, una caja. int es mucho más que eso: ahora tienes una referencia (4-8 bytes), el valor en sí (4) y los metadatos (que incluyen el identificador de tipo específico). Esto es muy diferente de, por ejemplo, C ++, que le permite lanzar cualquier puntero a un puntero de cualquier tipo (y dejar que usted se ocupe de los errores cuando lo emita de forma incorrecta).

De nuevo, todos los objetos por referencia tienen estos metadatos. Esta es una parte muy importante del costo de los tipos de referencia, pero también es el medio por el cual puede estar seguro del tipo de seguridad. Esto también muestra muy bien lo caro ArrayList de int realmente puede ser, y por qué int[] o List<int> es mucho más eficiente, incluso ignorando los costos de asignar (y lo que es más importante, recopilar) objetos de montón y el boxeo y el desempaquetado en sí, el byte de 4 bytes podría tener 20 bytes, simplemente porque está almacenando una referencia a él :)


1
2017-07-30 11:23



Preguntas populares