2009-12-22 14 views
12

Así que entiendo lo que es boxeo y unboxing. ¿Cuándo aparece en el código del mundo real, o en qué ejemplos es un problema? No me puedo imaginar haciendo algo como este ejemplo:Boxeo y desembalaje: ¿cuándo aparece?

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

... pero eso es casi seguro que muy simplificada y que podría haber hecho, incluso el boxeo/unboxing sin saberlo antes.

+0

Estoy de acuerdo con usted. De todo lo que he leído sobre el tema, simplemente no entiendo el punto. Debe faltar algo :) –

Respuesta

31

Es mucho menos de un problema ahora de lo que era antes de los genéricos. Ahora, por ejemplo, podemos usar:

List<int> x = new List<int>(); 
x.Add(10); 
int y = x[0]; 

No requiere boxeo ni desempaquetado.

Anteriormente, nos hemos tenido:

ArrayList x = new ArrayList(); 
x.Add(10); // Boxing 
int y = (int) x[0]; // Unboxing 

Esa era mi experiencia más común de boxeo y unboxing, por lo menos.

Sin la participación de los genéricos, creo que probablemente diría que la reflexión es la causa más común del boxeo en los proyectos en los que he trabajado. Las API de reflexión siempre usan "objeto" para cosas como el valor de retorno de un método, porque no tienen otra manera de saber qué usar.

Otra causa que podría atraparlo si no lo sabe es si usa un tipo de valor que implementa una interfaz, y pasa ese valor a otro método que tiene el tipo de interfaz como su parámetro. Una vez más, los genéricos hacen de este un problema menor, pero puede ser una sorpresa desagradable si no eres consciente de ello.

+7

"Creo que probablemente diría que la reflexión es la causa más común del boxeo en los proyectos en los que he trabajado"; esto, por supuesto, dependerá en gran medida del tipo de proyectos. Por ejemplo, si trabaja con WPF o Silverlight, el boxeo ocurrirá * todo el tiempo * mientras trabaja con convertidores de valor (IValueConverter toma y devuelve el objeto), propiedades de dependencia (DependencyObject.GetValue y SetValue return y take object), etc. – itowlson

+0

+1 Para la interfaz implementada en un tipo de valor, es un engañoso :) –

+0

@itowlson: Esos son excelentes ejemplos. ¿Te importa si los agrego a mi respuesta? –

8

Boxeo (en mi experiencia) por lo general se produce en estos casos:

  • un tipo de valor se pasa a un método que acepta un argumento de tipo Object.
  • Se agrega un tipo de valor a una colección no genérica (como un ArrayList).

Otras veces puede ver el boxeo y el desempaquetado es cuando utiliza la reflexión como la API de reflexión de .NET Framework hace un uso intensivo de Object.

+1

Debemos tener en cuenta: int (Int32) es una subclase de la clase abstracta ValueType, que es una subclase de Object. –

+0

No diría que es una "subclase", no es una * clase * en absoluto. Hereda de (o deriva de) 'ValueType'. Las especificaciones de C# y CLI usan terminología ligeramente diferente en este frente, lo que no ayuda. –

1

El boxeo y el desempaquetado se está moviendo realmente del tipo de valor al tipo de referencia. Por lo tanto, piense en ello como pasar de la pila al montón y viceversa.

Ciertamente hay casos donde esto es relevante. La inclusión de genéricos en el marco 2.0 cortó muchos casos comunes de boxeo fuera de la práctica.

+0

C# 2, no el framework 2.0;) – disklosr

0

Desde el advenimiento de listas fuertemente tipadas y diccionarios utilizando genéricos con C# 2.0 (Visual Studio 2005), creo que la importancia de mantener el boxeo/unboxing en cuenta se ha minimizado de forma asombrosa. Agregue a los tipos anulables (int?, etc.) y utilizando el operador coalescente nulo (??) y realmente no debería ser una gran preocupación y probablemente no lo vea en ningún código que no sea 1.1 Framework o anterior.

+2

No, en genéricos hay mucho más boxeo de lo que la mayoría de la gente esperaría. C# emitirá .box cuando compruebe Ts incontrastable contra nulo. –

+0

Estoy de acuerdo con eso, pero esa es la decisión del desarrollador al implementar un método/clase genérico. Desde una perspectiva de marco, las colecciones genéricas permiten al consumidor renunciar al escenario de boxeo/unboxing. Cuando codigo el escenario que describes, mi T no restringida se compararía con la predeterminada (T), no nula. Si realmente * necesito * una verificación contra nulo, T mejor se restringe a un tipo de referencia. –

+0

"en cualquier código que no sea 1.1 Framework o anterior" - ¿qué pasa con una cadena simple.Formato ("Mi número es {0}", 123)? – Alex

4

El boxeo/unboxing se produce cuando se pasa un tipo de valor (como una estructura, int, largo) en algún lugar que acepta un tipo de referencia, como object.

Esto ocurre cuando se crea explícitamente un método que toma parámetros de tipo objeto que se pasarán tipos de valores. También aparece cuando utiliza las colecciones antiguas no genéricas para almacenar tipos de valores (típicamente primitivos).

También verá el boxeo cuando usa String.Format() y le transfiere las primitivas. Esto se debe a que String.Format() acepta un objeto params [], lo que da como resultado el encajamiento de los parámetros adicionales en la llamada.

El uso de la reflexión para invocar métodos también puede dar como resultado boxing/unboxing, porque las API de reflexión no tienen otra opción que devolver object ya que el tipo real no se conoce en tiempo de compilación (y las Reflection API no pueden ser genéricas).

Las colecciones genéricas más nuevas no dan como resultado el boxeo/unboxing, por lo que son preferibles a las colecciones más antiguas por este motivo (por ejemplo, ArrayList, Hashtable, etc.). Sin mencionar que son seguros de tipo.

Puede evitar las preocupaciones del boxeo cambiando los métodos que aceptan objetos para ser genéricos. Por ejemplo:

public void string Decorate(object a) // passing a value type results in boxing 
{ 
    return a.ToString() + " Some other value"; 
} 

vs:

public void string Decorate<T>(T a) 
{ 
    return a.ToString() + " some other value"; 
} 
1

Sucede todo el tiempo en que la gente no sabe cuáles son las implicaciones, simplemente no les importa o, a veces uno no puede dejar de aceptar el boxeo como el menor Evel.

Los datarows fuertemente tipeados encajonarán/desempaquetarán prácticamente todo el tiempo cuando acceda a una propiedad de tipo valor. Además, al usar un tipo de valor como referencia de interfaz también se incluirá en la caja. O obtener un delegado de un método de instancia de un tipo de valor. (Objetivo del delegado es de tipo Object)

3

Aquí es una realidad desagradable una :)

SqlCommand cmd = <a command that returns a scalar value stored as int>; 

// This code works very well. 
int result = (int)cmd.ExecuteScalar(); 

// This code will throw an exception. 
uint result = (uint)cmd.ExecuteScalar(); 

La segunda ejecutar falla porque se trata de desempacar un Int32 en un UInt32 que no es posible. Entonces debes primero desempaquetar y luego lanzar.

uint result = (uint)(int)cmd.ExecuteScalar(); 
+0

+1 por dar un ejemplo – chikak