Desenvolvimento - C#
Uma discussão sobre tipos – boxing e unboxing em C#
O Paradigma de programação orientada a objetos nos traz muitas facilidades por permitir que pensemos o mais próximo possível da vida real quando vamos desenvolver um sistema. Mas como tudo na vida, pensar que “tudo é um objeto” pode trazer problemas.
por Marden MenezesSe pensarmos em uma linguagem totalmente orientada a objetos, tudo seria objeto, inclusive tipos simples que não precisam de tanto “overhead” de memória. Números são um bom exemplo disso. Caso precisemos somar 2 números em uma linguagem totalmente OO, precisaríamos criar um objeto que representasse o primeiro número, outro que representasse o segundo e somar os dois colocando o resultado em outro. Gastaríamos então muito processamento para uma operação simples que até um hardware modesto pode fazer. Esse problema ocorre normalmente em linguagens como Smalltalk, que segue totalmente a OOP.
A linguagem Java resolveu isso de uma maneira interessante. Em Java, temos tipos por referência (objetos comuns) e tipos primitivos , que não são objetos, apenas contêm o valor dos seus dados e servem para funcionalidades que não precisam gastar muita memória. Os tipos primitivos, em Java, não podem chamar métodos, o que prejudica em muitos casos. A solução para isso é o que chamamos de Wrapper Classes (classes invólucros), que são objetos que “transformam” tipos primitivos de Java em tipos por referência. O problema é que tudo isso é feito pelo desenvolvedor, que tem que usar o invólucro literalmente “na mão”, além do fato de que tipos primitivos não permitem um sistema com ancestral comum, ou seja, nem tudo em Java herda de Object.
C#, através do .NET Framework, resolve esse problema de uma forma mais elegante através do uso de Boxing e Unboxing. Na prática, o que acontece, é que a transformação de um tipo por valor de C# (equivalente ao tipo primitivo de Java) para um tipo por referência é feito automaticamente, em um processo chamado Boxing. O processo de volta é chamado de Unboxing. Podemos dizer então que, na prática, tudo em C# é visto como um objeto, e essa transformação é automática.
Boxing
No processo de boxing o que acontece é que um novo objeto é alocado na memória e o valor da variável é copiado para ele. Na prática, o que acontece é que pegamos o tipo por valor e colocamos dentro de um novo objeto. A partir desse momento podemos utilizar o nosso tipo por valor como um tipo por referência.
Se virmos o código abaixo podemos entender melhor o processo:
public class Class1 { static void Main(string[] args) { //criando um tipo por valor int valor = 10; //o fato de chamar o método GetType() obriga o Boxing valor.GetType(); } }
Na plataforma .NET, o código de qualquer linguagem é compilado para uma linguagem intermediária chamada MSIL (Microsoft Intermediate Language) , permitindo assim a multiliguageme a multiplataforma. O código MSIL pode ser visto a partir de um aplicativo chamado ILDASM (Intermediate Language Disassembler). Esse aplicativo vem junto com o .NET Framework SDK.
Para vermos o código MSIL, abrimos o ILDASM a partir do prompt de comando do Visual Studio .NET digitando “ildasm”. Após aberto, podemos verificar qualquer DLL ou EXE produzido na plataforma .NET. Esse é o código MSIL do código acima:
Podemos perceber na linha selecionada que houve uma chamada ao procedimento de box, que faz o boxing do tipo inteiro para um objeto Int32.
Unboxing
O processo de unboxing é exatemente o contrário do boxing. Para executar ações em que não é necessário o peso de um tipo por referência, o unboxing traz de volta o tipo por valor. Funciona como se nós tivéssemos retirado o invólucro da nossa variável e a deixássemos apenas com o seu valor.
Veja o código abaixo:
class Class1 { static void Main(string[] args) { int a = 0; //tipo por valor object b = a; // boxing int c = (int) b; // unboxing } }
Podemos perceber que o fato de querermos colocar um objeto b em uma variável por valor c obriga o chamado do unboxing, que pode ser visto na MSIL desse código:
Problemas
Nem sempre algo ser feito automaticamente para nós é melhor. Temos que lembrar que a passagem de valor para referência é um processo custoso e que, em aplicações críticas devemos evitar isso.
Se formos analisar o código abaixo, podemos perceber que, ao chamarmos o método Write, o inteiro i será transformado em objeto automaticamente 3 vezes (perceba isso na MSIL mais abaixo). Você não acha isso uma gasto extremamente desnecessário?
static void Main(string[] args) { int i = 123; //acontecem 3 boxing! Console.Write("valor:"+ i + i + i); }
Vejam o código MSIL, nele você pode perceber nas linhas selecionadas que há a execução de 3 box para transformar i em um objeto.
Uma solução elegante para esse problema é obrigarmos a execução de apenas um boxing, fazendo como descrito no código a seguir:
static void Main(string[] args) { int i = 123; object o = i; // boxing // usando o objeto criado ao invés do tipo por valor Console.Write("valor:"+ o + o + o); }
Assim resolvemos o problema!
Conclusão
C# tem realmente um sistema de tipo totalmente orientado a objetos já que qualquer tipo por valor pode ser trabalho como tipo de referência através da transformação transparente realizada pelo processo de boxing e unboxing. Apesar de fazer o processo ficar mais cômodo para o desenvolvedor, algumas vezes é bom ter cuidado para não usar processamento desnecessário.
AVISO:
Pessoal, o meu grupo de usuários está completando 1 ano em dezembro e está realizando grandes eventos em Recife! Entrem no site www.sharpshooters.org.br e se inscrevam!