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 Menezes



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.

Se 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!

Marden Menezes

Marden Menezes - Líder do maior grupo de usuários do Brasil, o Sharp Shooters (www.sharpshooters.org.br). Marden é Microsoft Certified Professional e Microsoft Student Ambassador, ministrando palestras e cursos em todo o Brasil e difundindo a plataforma .NET nas universidades.
Como representante do comitê de relacionamento com grupos de usuários da INETA (www.ineta.org), Marden vem trabalhando para a difusão dos grupos de usuários .NET no país.