Fala galera, blz?

Sexta publicação da série introdução ao .Net Framework. Nesse post falarei sobre boxing e unboxing de tipos. Você sabia que pode estar realizando operações que podem estar diminuindo a performance do seu código?

Para quem não leu os post anteriores, recomendo que leiam, segue os link’s.

1 - Introdução ao .Net Framework - Parte 1 - Do que é composto, compialçao.

2 - Introdução ao .Net Framework - Parte 2 - CLR, CTS, FCL, CLS.

3 - Introdução ao .Net Framework - Parte 3 - Tipos.

4 - Introdução ao .Net Framework - Parte 4 - Conversão/Casting de tipos.

5 - Introdução ao .Net Framework - Parte 5 - Namespace e Assemblies.

INTRODUÇÃO 

Diferente dos tipos de referência os tipos de valor não são alocados na memória heap, não são referenciados e não sofrem coleta de lixo. Até aqui está claro que um tipo valor, quando falamos em desempenho, é melhor que um tipo-referência. Então porque não utilizamos mais tipos de valor? Existem várias respostas para essa pergunta, mas, em minha opinião, o principal fator é a facilidade de se trabalhar com classes.

Já sabemos como funciona a conversão entre um tipo de referência para outro tipo referência e de um tipo-valor para outro tipo-valor. Agora vamos entender como o framework se comporta na conversão de um tipo valor para um tipo-referência. Esse tipo de conversão é chamado de Boxing.

BOXING DE TIPOS-VALOR

É comum declararmos uma variável de tipo valor e em algum momento precisar obter uma referência da instancia criada desse tipo. Irei mostrar um exemplo passo a passo para ficar fácil o entendimento. Veja o código abaixo:

C#

[sourcecode language="csharp"]

public class MinhaClasse
{
//declarando uma struct
//struct é um tipo valor
struct ExemploStruct
{
public int a, b;
}

void Main()
{
//declarando um objeto ArrayList
//esse objeto se encontra no namespace System.Collections
ArrayList Aux = new ArrayList();

//declarando uma variavel do tipo ExemploStruct
//Como esse tipo é uma struct
//seu valor será guardado na memória stack
ExemploStruct Ex;

}

}
[/sourcecode]

No código acima foi criada a classe MinhaClasse e dentro da classe foi criado um tipo struct chamado ExemploStruct e um método void, por enquanto você só precisa saber que void é um tipo de método que não tem retorno, chamando Main. O tipo struct é um tipo valor que é tipicamente usado para encapsular pequenos grupos de variáveis, como por exemplo, coordenadas ou características de um retângulo.  Dentro de ExemploStruct foram criadas duas variáveis do tipo System.Int32, “a” e “b”. No método Main() foi criado um objeto do tipo ArrayList e logo abaixo declarado uma variável do tipo ExemploStruct. O tipo ArrayList está presente dentro do namespace System.Collections. Com esse tipo é possível se trabalhar com lista, sendo que, seu tamanho pode ser aumentando de acordo com a necessidade. O ArrayList possui alguns métodos e um deles é muito usado, o Add. O método Add adiciona um objeto no final do ArrayList. Veja agora o mesmo código com mais um trecho implementado, usando o método Add:

C#

[sourcecode language="csharp"]

public class MinhaClasse
{
//declarando uma struct
//struct é um tipo valor
struct ExemploStruct
{
public int a, b;
}

void Main()
{
//declarando um objeto ArrayList
//esse objeto se encontra no namespace System.Collections
ArrayList Aux = new ArrayList();

//declarando uma variavel do tipo ExemploStruct
//Como esse tipo é uma struct
//seu valor será guardado na memória stack
ExemploStruct Ex;

for (int i = 0; i < 5; i++)
{
Ex.a = 1; //Atribuindo valor a variavel a do tipo ExemploStruct
Ex.b = 2; //Atribuindo valor b variavel a do tipo ExemploStruct
Aux.Add(Ex);
}

}

[/sourcecode]

Veja que foi criado um laço de repetição usando um for. Nesse laço eu inicializado as variáveis do tipo ExemploStruct e uso o método Add do meu objeto ArrayList para adicionar a instancia de ExemploStruct dentro de sua lista. Todo loop que ocorrer dentro desse for uma nova instancia de ExemploStruct será criada e adicionada dentro do ArrayList. Lembra quando eu disse quando que o método Add do tipo ArrayList adiciona um objeto no final da lista? Lembram também que um objeto é uma referência (ponteiro) que aponta para o local onde está armazenado seus valores na memória heap? Ou seja, para que o método Add funcione vamos ter que converter o tipo ExemploStruct em um objeto, essa conversão é chamada de boxing. Mas, só precisamos fazer essa conversão explicitamente em algumas linguagens, como o C++. O compilador do C# faz isso automaticamente!

Lembrando que, para se trabalhar com objetos, mais recursos de processamento são usados como foi explicado no início desse post.  Tranquilo o boxing  né? Vejamos agora o Unboxing.

UNBOXING DE TIPOS VALOR

Ainda analisando o código usado como exemplo para entendermos o boxing, imagine que em um momento da aplicação seja necessário obter um determinado valor do ArrayList, como isso seria feito? Veja o exemplo abaixo:

C#

[sourcecode language="csharp"] 

//Declarando um tipo valor ExemploStruct
ExemploStruct ExUnboxing;

/*Atribuindo o primeiro valor do ArrayList para a variavel ExUnboxing
usando casting*/
ExUnboxing = (ExemploStruct) Aux[0];

[/sourcecode]

Neste exemplo estou pegando o ponteiro contido no objeto do primeiro elemento do ArrayList e tentando coloca-lo na variável de tipo valor ExUnboxing. Para isso funcionar o CLR realizará dois passo.

1. Obter o endereço do objeto (boxed) na memória heap, esse primeiro passo é chamado do unboxing.

2. Os valores armazenados na memória heap são copiados para a instancia criada de tipo valor armazenada na memória stack.

Se juntarmos esses dois passos realizados a operação de unboxing se torna exatamente o oposto de boxing, entretanto, não se confunda, pois, o processo denominado unboxing é somente o primeiro.

Reforçando o que eu já disse, as operações realizadas no boxing e unboxing podem causar uma queda de desempenho na aplicação. Deve-se tomar cuidado com o casting realizado no unboxing para que exceções não sejam disparadas caso o objeto contido no elemento do ArrayList seja nulo ou de um tipo diferente do tipo que o receberá.

CONCLUSÃO

Em alguns momentos pode ser necessário realizar o boxing de um tipo valor para poder utilizar um método que requer um tipo referência. Eu mostrei um exemplo com essa publicação, mas, não quer dizer que seja o único.

Espero que esse conteúdo seja útil para alguém.

Até o próximo post!

REFERÊNCIAS

               struct (C# Reference) - http://msdn.microsoft.com/en-us/library/ah19swz4.aspx

               Classe ArrayList - http://msdn.microsoft.com/pt-br/library/system.collections.arraylist(v=vs.110).aspx

              Programação aplicada com Microsoft .Net Framework – Jeffrey Richter