¿Qué es boxing y unboxing y cuáles son las compensaciones?


135

Estoy buscando una respuesta clara, concisa y precisa.

Idealmente como la respuesta real, aunque los enlaces a buenas explicaciones son bienvenidos.


2
¿Es esto realmente agnóstico del lenguaje?
Henk Holterman

3
@HenkHolterman ciertamente no es un idioma específico, aunque tampoco es relevante para todos los idiomas: la distinción será irrelevante para la mayoría de los idiomas de tipo dinámico, por ejemplo. No estoy seguro de qué etiqueta podría usarse en su lugar language-but-not-type-agnostic. static-language-agnostic? No estoy seguro de que SO necesite la distinción; Sin embargo, podría ser una buena pregunta para el meta.
Keith

Respuestas:


189

Los valores en recuadro son estructuras de datos que son envoltorios mínimos alrededor de los tipos primitivos *. Los valores en recuadro generalmente se almacenan como punteros a los objetos en el montón .

Por lo tanto, los valores en recuadro usan más memoria y toman como mínimo dos búsquedas de memoria para acceder: una para obtener el puntero y otra para seguir ese puntero a la primitiva. Obviamente, este no es el tipo de cosa que quieres en tus circuitos internos. Por otro lado, los valores en recuadro suelen jugar mejor con otros tipos en el sistema. Como son estructuras de datos de primera clase en el lenguaje, tienen los metadatos y la estructura esperados que tienen otras estructuras de datos.

En Java y Haskell, las colecciones genéricas no pueden contener valores sin caja. Las colecciones genéricas en .NET pueden contener valores sin caja sin penalizaciones. Cuando los genéricos de Java solo se usan para la verificación de tipos en tiempo de compilación, .NET generará clases específicas para cada tipo genérico instanciado en tiempo de ejecución .

Java y Haskell tienen matrices sin caja, pero son claramente menos convenientes que las otras colecciones. Sin embargo, cuando se necesita un rendimiento máximo, vale la pena un pequeño inconveniente para evitar la sobrecarga de boxeo y unboxing.

* Para esta discusión, un valor primitivo es cualquiera que pueda almacenarse en la pila de llamadas , en lugar de almacenarse como un puntero a un valor en el montón. Con frecuencia, son solo los tipos de máquina (ints, flotantes, etc.), estructuras y, a veces, matrices de tamaño estático. .NET-land los llama tipos de valor (a diferencia de los tipos de referencia). La gente de Java los llama tipos primitivos. Haskellions simplemente los llama sin caja.

** También me estoy centrando en Java, Haskell y C # en esta respuesta, porque eso es lo que sé. Por lo que vale, Python, Ruby y Javascript tienen valores exclusivamente encuadrados. Esto también se conoce como el enfoque "Todo es un objeto" ***.

*** Advertencia: un compilador / JIT suficientemente avanzado puede detectar en algunos casos que un valor que está semánticamente encuadrado al mirar la fuente, puede ser un valor sin caja en tiempo de ejecución. En esencia, gracias a los brillantes implementadores de lenguaje, sus cajas a veces son gratuitas.


¿Por qué a pesar de un valor encuadrado, qué beneficio obtiene el CLR o lo que sea, obtener valores de boxeo en forma?
PositiveGuy

En resumen (ja, ja), son solo otro Objeto, lo cual es muy conveniente. Las primitivas (al menos en Java) no descienden del Objeto, no pueden tener campos, no pueden tener métodos, y generalmente se comportan de manera muy diferente a otros tipos de valores. Por otro lado, trabajar con ellos puede ser muy rápido y eficiente en cuanto al espacio. Por lo tanto, la compensación.
Peter Burns el

2
Javascript tiene los llamados arreglos mecanografiados (nuevo UInt32Array, etc.) que son arreglos de ints y flotantes sin caja.
nponeccop

126

de C # 3.0 en pocas palabras :

El boxeo es el acto de convertir un tipo de valor en un tipo de referencia:

int x = 9; 
object o = x; // boxing the int

unboxing es ... lo contrario:

// unboxing o
object o = 9; 
int x = (int)o; 

72

Boxing & unboxing es el proceso de convertir un valor primitivo en una clase de contenedor orientado a objetos (boxing), o convertir un valor de una clase de contenedor orientado a objetos al valor primitivo (unboxing).

Por ejemplo, en Java, es posible que deba convertir un intvalor en un Integer(boxeo) si desea almacenarlo en un Collectionporque las primitivas no se pueden almacenar en un Collectionsolo objeto. Pero cuando desee recuperarlo, Collectiones posible que desee obtener el valor como inty no como, Integerpor lo que lo desempaquetará.

El boxeo y el desempaquetado no es inherentemente malo , pero es una compensación. Dependiendo de la implementación del lenguaje, puede ser más lento e intensivo en memoria que solo usar primitivas. Sin embargo, también puede permitirle utilizar estructuras de datos de nivel superior y lograr una mayor flexibilidad en su código.

En estos días, se discute más comúnmente en el contexto de la función de "autoboxing / autounboxing" de Java (y de otros idiomas). Aquí hay una explicación centrada en Java del autoboxing .


23

En la red:

A menudo no puede confiar en el tipo de variable que consumirá una función, por lo que debe usar una variable de objeto que se extienda desde el mínimo común denominador, en .Net esto es object.

Sin embargo, objectes una clase y almacena su contenido como referencia.

List<int> notBoxed = new List<int> { 1, 2, 3 };
int i = notBoxed[1]; // this is the actual value

List<object> boxed = new List<object> { 1, 2, 3 };
int j = (int) boxed[1]; // this is an object that can be 'unboxed' to an int

Si bien ambos contienen la misma información, la segunda lista es más grande y más lenta. Cada valor en la segunda lista es en realidad una referencia a un objectque contiene el int.

Esto se llama en caja porque intestá envuelto por object. Cuando se vuelve a lanzar, intse desempaqueta: se vuelve a convertir en su valor.

Para los tipos de valor (es decir, todos structs) esto es lento y potencialmente usa mucho más espacio.

Para los tipos de referencia (es decir, todos classes) esto es mucho menos problemático, ya que de todos modos se almacenan como referencia.

Otro problema con un tipo de valor en caja es que no es obvio que se trata de la caja, en lugar del valor. Cuando compara dos, structsentonces está comparando valores, pero cuando compara dos, classesentonces (por defecto) está comparando la referencia, es decir, ¿son la misma instancia?

Esto puede ser confuso cuando se trata de tipos de valores encuadrados:

int a = 7;
int b = 7;

if(a == b) // Evaluates to true, because a and b have the same value

object c = (object) 7;
object d = (object) 7;

if(c == d) // Evaluates to false, because c and d are different instances

Es fácil evitarlo:

if(c.Equals(d)) // Evaluates to true because it calls the underlying int's equals

if(((int) c) == ((int) d)) // Evaluates to true once the values are cast

Sin embargo, es otra cosa a tener en cuenta cuando se trata de valores encuadrados.


1
En vb.net, la distinción entre semántica de igualdad es más clara, Objectno implementa el operador de igualdad, pero los tipos de clase se pueden comparar con el Isoperador; por el contrario, Int32se puede usar con el operador de igualdad, pero no Is. Esa distinción hace mucho más claro qué tipo de comparación se está haciendo.
supercat

4

Boxinges el proceso de conversión de un tipo de valor en un tipo de referencia. Mientras que Unboxinges la conversión de un tipo de referencia en un tipo de valor.

EX: int i = 123;
    object o = i;// Boxing
    int j = (int)o;// UnBoxing

Tipos de valor son: int, chary structures, enumerations. Tipos de referencia son: Classes, interfaces, arrays, stringsyobjects


3

Las colecciones genéricas de .NET FCL:

List<T>
Dictionary<TKey, UValue>
SortedDictionary<TKey, UValue>
Stack<T>
Queue<T>
LinkedList<T>

Todos fueron diseñados para superar los problemas de rendimiento del boxeo y el unboxing en implementaciones de colecciones anteriores.

Para obtener más información, consulte el capítulo 16, CLR a través de C # (2a edición) .


1

El boxeo y el desempaquetado facilitan que los tipos de valor sean tratados como objetos. El boxeo significa convertir un valor a una instancia del tipo de referencia de objeto. Por ejemplo, Intes una clase y intes un tipo de datos. La conversión inta Intes un ejemplo de boxeo, mientras que la conversión Inta intes unboxing. El concepto ayuda en la recolección de basura, Unboxing, por otro lado, convierte el tipo de objeto en tipo de valor.

int i=123;
object o=(object)i; //Boxing

o=123;
i=(int)o; //Unboxing.

En javascript, var ii = 123; typeof ii devuelve number. var iiObj = new Number(123); typeof iiObjvuelve object. typeof ii + iiObjvuelve number. Así que este es el equivalente de JavaScript del boxeo. El valor iiObj se convirtió automáticamente en un número primitivo (sin caja) para realizar la aritmética y devolver un valor sin caja.
PatS

-2

Como cualquier otra cosa, el autoboxing puede ser problemático si no se usa con cuidado. El clásico es terminar con una NullPointerException y no poder rastrearla. Incluso con un depurador. Prueba esto:

public class TestAutoboxNPE
{
    public static void main(String[] args)
    {
        Integer i = null;

        // .. do some other stuff and forget to initialise i

        i = addOne(i);           // Whoa! NPE!
    }

    public static int addOne(int i)
    {
        return i + 1;
    }
}

Este es solo un código incorrecto y no tiene nada que ver con el autoboxing. La variable ise inicializa prematuramente. Conviértalo en una declaración vacía ( Integer i;) para que el compilador pueda señalar que olvidó inicializarlo, o espere para declararlo hasta que sepa su valor.
erickson

Hmm, y si hago algo entre dentro de un bloque try try, entonces el compilador me obligará a inicializarlo con algo. Este no es un código real, es un ejemplo de cómo podría suceder.
PEELY

¿Qué demuestra esto? No hay absolutamente ninguna razón para usar el objeto Integer. En cambio, ahora tiene que lidiar con un potencial NullPointer.
Richard Clayton
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.