Recapitulando
Essa série tem por objetivo os apresentar fundamentos da programação em Intermediate Language. O que já vimos até aqui foi:
- Parte 8: Construtores para ValueTypes e ReferenceTypes
- Parte 7: Atributos e propriedades
- Parte 6: Variáveis locais e loops
- Parte 5: Trabalhando com parâmetros
- Parte 4: Classes e namespaces
- Parte 3: Equilíbrio Performance e Memória em IL
- Parte 2: Estrutura da máquina virtual IL
- Parte 1: Apresentando IL
Sobre essa parte
Nessa parte, apresento uma breve introdução a boxing e unboxing. Logo depois, apresento as instruções disponíveis em Intermediate Language para o procedimento dessas operações.
O que é boxing/unboxing?
Boxing é o processo de conversão de value type para objeto. Quando a CLR realiza um boxing, na prática, envolve o valor em um System.Object e armazena isso no heap gerenciado. O processo de Unbox extraí o valor desse objeto, devolvendo-o para a Evaluation Stack.
Uma boa descrição para boxing e unboxing pode ser encontrada aqui.
Operações Boxing/Unboxing são “caras” e devem ser evitadas. Sempre que um value type é passa pelo processo de boxing, uma instância de objeto inteiramente nova é criada. De forma semelhante, a operação de casting no processo de unboxing, ainda que menos que no boxing, também consume tempo de processamento. Segundo o msdn, o processo de boxing consume 20 vezes mais tempo que uma atribuição simples. Unboxing consome 4 vezes mais tempo.
Fundamentos de boxing e unboxing em Intermediate Language
Fazer boxing de ValueTypes em Intermediate Language é realmente fácil: Basta usar a instrução box. Por um exemplo, se desejamos pegar um valor inteiro, armazenado em uma variável local, e fazer boxing com ele, poderíamos escrever algo assim:
1 ldloc.0 2 box int32
Como já sabemos, nós utilizamos o keyword int32 como um resumo para o tipo. Obviamente, para tipos não-primitivos devemos indicar o tipo explicitamente. Observe:
1 ldloc.0 2 box [System.Drawing]System.Drawing.Color
Ao executar essa operação, [no box] o valor é retirado da Evaluation Stack sendo colocado em seu lugar uma referência para o objeto.
Realizar o unboxing é simples também. Observe:
1 unbox int32
Usa-se a instrução unbox que retira a referência para o objeto da pilha e coloca em seu lugar um ponteiro gerenciado para o dado. A instrução unbox também requisita que informemos o tipo do dado que estamos trabalhando.
A instrução Unbox não realiza a operação inversa de Box
Opa! Supresa. A operação executada pela instrução unbox não é inversa a operação executada pela instrução box. Como destacado, o box pega uma instância de ValueType da pilha, unbox devolve um endereço – um ponteiro gerenciado. E isso revela alguns aspectos importantes:
- unbox não é, realmente, o inverso de box;
- box involve a cópia de dados, mas, unbox não;
- box retira o valor do evaluation stack, ou do heap local, e cria um objeto no heap gerenciado com seu conteúdo, colocando uma referência para ele na Evaluation Stack
- unbox retira a referência e coloca um apontador simples (ponteiro gerenciado) que aponta para o local do dado.
Diferença entre referências para objetos e ponteiros gerenciados
Um object é uma referência para um objeto no heap gerenciado. Um ponteiro gerenciado pode fazer referência tanto para dados do heap gerenciado, quanto da evaluation stack.
Ponteiros gerenciados foram “planejados” para “apontar” para o dado em si, enquanto um object aponta para estrutura de dados (o dado relacionado, precedido por um cabeçalho) relacionada ao object. ValueTypes, por padrão, não possuem essa estrutura de dados e, por isso, consomem menos memória (geralmente 4 bytes por objeto).
Executando a operação inversa do Box (unboxing)
Bem, já sabemos que a instrução unbox não realiza a operação inversa do box. Mas, digamos que queiramos fazer isso. Ou seja, realizar o unbox e trazer uma cópia do valor devolta para o Evalution Stack. Como fazer isso? A resposta está no uso [complementar] da família de instruções ldind.*. Segue resumo:
- ldind.i1/ldind.i2/ldind.i4/ldind.u1/ldind.u2/ldind.u4 – faz uma cópia do valor int8/int16/int32/unsigned int8/ unsigned int16/ unsigned int32 apontado por um ponteiro gerenciado, para um valor int32 na evaluation stack;
- ldind.i8 – faz uma cópia do valor int64 apontado por um ponteiro gerenciado, para um valor int64 na evaluation stack;
- ldind.r4/ldind.r8 – faz uma cópia do valor float apontado por um ponteiro gerenciado, para um valor float na evaluation stack;
Para outros ValueTypes, deve-se usar a instrução ldobj.
O trecho seguinte mostra como executar operação inversa do boxing. Ou seja, unboxing + cópia do valor para evaluation stack. Além disso, armazena o valor em uma variável local. Observe:
1 unbox int32 2 ldind.i4 3 stloc.0
Concluindo
Boxing/Unboxing são causa comum para performance pobre de nossas aplicações e, por isso, devem ser evitadas. Como podemos ver, os projetistas da Intermediate Language tentaram reduzir ao máximo o impacto dessas operações. Como foi visto, o unboxing aproveita o conteúdo presente no Heap Gerenciado. Se o CLR faz a parte dele, façamos a nossa!
Por hoje, é isso ![]()






Wagner Leão
01/09/2010
Espetacular post!
Muito bom seu blog.