Trabalhando com código não-gerenciado

                                                              

Introdução

Felizmente a equipe da plataforma .Net e C# tornaram fácil a vida dos programadores que desejam interoperar com código existente pelo uso de código não-gerenciado. O código não-gerenciado da plataforma .Net e C# refere-se ao código que não é gerenciado e nem controlado em tempo de execução pela .Net.

 

Neste artigo veremos um dos principais itens de código não-gerenciado na plataforma .Net que é o código inseguro.

 

O código inseguro nos permite escrever código em C# utilizando construções, como os ponteiros, nos nossos aplicativos em C#.

 

Qualquer programador em C ou C++ sabe o quanto pode ser útil a utilização de ponteiros, por isso mesmo C# sendo uma linguagem muito segura nos fornece essa possibilidade de trabalhar com ponteiros, ai que entra o código inseguro no C#. Apesar do nome “código inseguro”, isso não significa que seja um código perigoso, ou então um código não confiável, na realidade trata-se apenas de um código na qual em tempo de execução a plataforma .Net não controla a alocação e a desalocação de memória. Esse é um dos grandes motivos pela utilização de ponteiros ser chamada de código inseguro, imagine m programador alocar vários megabytes de memória e esquecer de desalocar toda essa quantidade de memória usada, poderia se tornar uma catástrofe para qualquer aplicação.

 

A grande vantagem da utilização de código inseguro é quando queremos utilizar ponteiros para comunicação com código de legado, ou então por questão de desempenho quando seu aplicativo manipula de forma direta a memória.

 

Utilizando o código inseguro

Para utilizarmos código inseguro buscamos duas palavras chaves, são elas unsafe e fixed.

 

De forma geral a palavra-chave unsafe indica que o bloco marcado executará em um contexto na qual não é gerenciado. Utilizamos a palavra-chave em métodos, e blocos de código dentro dos próprios métodos.

 

A palavra-chave fixed é responsável por indicar ao garbage collector que o objeto não pode ser movido. O que acontece aqui é que a plataforma .Net move os objetos na memória para tornar a utilização dela mais eficiente. Essa é uma palavra-chave que deve ser usada com muito cuidado, visto que os objetos são movidos na memória para melhorar a eficiência podemos causar estragos, imagine que temos vários ponteiros alocados numa memória e usamos a palavra-chave em questão, o que faria com que os endereços de memórias dos ponteiros fossem movidos para outros locais de memória, tornando assim os ponteiros inválidos.

 

Utilizando ponteiros

Os ponteiros utilizados em C# são os mesmos que e utilizando em C e C++. Assim o “&” retorna um ponteiro que representa o endereço de memória da variável. O operador “*” utiliza-se para denotar o valor indicado pelo ponteiro. E por último o operador “->” é utilizado para acesso de membro e desreferenciamento de ponteiro.

 

Operador

Significado

&

Retorna um ponteiro que será um endereço de memória da variável.

*

Denota o valor indicado pelo ponteiro

->

Acessa um membro e desreferencia o ponteiro

 

Exemplificando ponteiros em unsafe

 

Vamos fazer dar um exemplo com a utilização de ponteiros no modo unsafe. Neste exemplo observamos as variáveis antes da função Recebe() e depois dela. Exemplificando assim a utilização valiosa de ponteiros executando em código inseguro. Não se esqueça que para compilar em código inseguro devemos utilizar a opção /unsafe do compilador.

 

using System;

using System.Collections.Generic;

using System.Text;

 

namespace ConsoleApplication3

{

    class Program

    {

        public static unsafe void Recebe(int* x, int* y)

        {

            *x = 11;

            *y = 22;

        }

 

        static unsafe void Main(string[] args)

        {

            int a = 1;

            int b = 2;

            System.Console.WriteLine("Antes de chamar a função Recebe():

            a = {0}, b = {1}", a, b);

            Recebe(&a, &b);

            System.Console.WriteLine("Depois de chamar a função Recebe():

            a = {0}, b = {1}", a, b);

            System.Console.ReadLine();

        }

    }

}

 

A saída do nosso exemplo acima deverá ser da seguinte maneira:

Antes de chamar a função Recebe(): a = 1, b = 2

Depois de chamar a função Recebe(): a = 11, b = 22

           

Utilizando o fixed

A instrução fixed usa-se com a sintaxe fixed(tipo *ponteiro = expressão) instrução. Para ficar mais simples o entendimento da instrução, vamos a um exemplo prático de como utilizar a instrução fixed.

 

using System;

using System.Collections.Generic;

using System.Text;

 

namespace ConsoleApplication3

{

    class Futebol

    {

        public int x;

    }

    class Program

    {

        unsafe static void SetarFutebolValor(int *x)

        {

            Console.WriteLine("Desreferenciado o ponteiro para modificar

            futebol.x");

            *x = 42;

        }

 

        unsafe static void Main(string[] args)

        {

            Futebol futebol = new Futebol();

            Console.WriteLine("futebol.x inicializa com {0}", futebol.x);

            fixed (int* f = &futebol.x)

            {

                Console.WriteLine("Chamando SetarFutebolValor() passando

                ponteiro para futebol.x");

                SetarFutebolValor(f);

            }

            Console.WriteLine("Depois retorna para SetarFutebolValor(),

            futebol.x = {0}",futebol.x);

            System.Console.ReadLine();

        }

    }

}

 

Este simples código mostra como podemos utilizar a instrução fixed. A instrução fixed no código acima fica o objeto “futebol” até a instrução composta envolvente terminar. Nesta linha “fixed(int* f = &futebol.x)” podemos ver a atribuição de um endereço do objeto “futebol” a um “Futebol*”.

 

Devemos salientar que a instrução fixed é utilizada somente para inclusão de um código que será afetado se o Garbage Collector fosse o mover o objeto futebol. Isso é importante em casos de termos blocos de código maiores com grandes quantidades de códigos onde queremos minimizar a quantidade de tempo em que tem um objeto fixado.