Desenvolvimento - C#
C#, uma linguagem para o novo milênio
O C# (pronuncia-se “c sharp) é uma nova linguagem criada pela Microsoft em conjunto com a arquitetura “.NET. A sintaxe do C# é muito parecida com a do C++. A princípio, quando uma construção do C++ não oferece problemas, ela é usada...
por Mauro Sant'AnnaO C# (pronuncia-se “c sharp) é uma nova linguagem criada pela Microsoft em conjunto com a arquitetura “.NET.
A sintaxe do C# é muito parecida com a do C++. A princípio, quando uma construção do C++ não oferece problemas, ela é usada. Este é o caso, por exemplo, nas declaração de variáveis e “loops”. Mas o C# modifica bastante o C++ e não tem a pretensão de manter a compatibilidade, apenas a “familiaridade”.
As principais características do C# são as seguintes:
- Todas as variáveis e código são declarados no escopo de classes. É possível, contudo, declarar tipos (“structs” e enumerações) fora do escopo de classes. Nem tudo é uma classe...
- Tipagem forte. As enumerações são tipos próprios e incompatíveis com outras enumerações. Existe um tipo lógico (bool) incompatível com inteiros. Os tipos intrínsecos são: lógico, inteiros de vários tamanhos pré-definidos (8, 16, 32 e 64 bits, com e sem sinal), ponto flutuante IEEE de 4 e 8 bytes, string e decimal. Só existe um único tipo “char”, também incompatível com inteiros. Tanto o “char” como a “string” armazenam apenas caracteres Unicode (16 bits por caractere). O tipo “decimal” é armazenado como uma mantissa binária de 96 bits e um expoente na base 10, para um total de 128 bits. A precisão do decimal é de pelo menos 28 dígitos decimais, o que evita a maioria dos erros de arredondamento comuns aos formatos de ponto flutuante em binário. Existe também “structs”, boas para serem usadas em situações “leves”, como por exemplo, uma coordenada (X, Y), quando o custo em memória e tempo de execução de uma classe seria grande e desnecessário.
- Os objetos e “arrays” são necessariamente alocados dinamicamente no “heap” com o uso do operador “new”.
- O índice dos “arrays” começa com zero e sua faixa é sempre verificada em tempo de execução.
- O C# inicializa a maioria das variáveis com zero e efetua diversas verificações de lógica, como se uma variável foi atribuida antes de ser usada, se um parâmetro de saída foi atribuído e se um inteiro teve sua faixa violada.
- Todas as conversões de tipo (“cast”) são validadas em função do tipo real da variável em tempo de execução, sem exceções.
- O operador “.” ‘é usado em diversos lugares, quando em C++ seriam usados “.”, “::” e “->”.
- Existe um outro tipo de loop além dos oriundos do C (for, while, do..while), o “foreach”, usado para varrer todos os elementos de um array ou “coleção”.
- O “switch” elenca opções mutuamente exclusivas, por definição, e pode ser usado com strings. O “break” depois de cada opção é obrigatório.
- O único mecanismo de tratamento de erros do C# é a exception.
- Não existem macros, mas existe compilação condicional (#ifdef, etc).
- Os templates não são suportados, pelo menos por enquanto. Talvez seja possível criar um mecanismo semelhante aos templates no futuro. De qualquer forma, o C# tem um suporte bastante abrangente a “reflections”, o que pode substituir templates em várias situações.
- O C# suporta sobrecarga de funções e de operadores, como o C++, mas não tem argumentos “default”.
- O C# possui operadores de conversão, mas existe uma sintaxe para indicar se a conversão deve ser implícita ou explícita. O construtor não é usado como operador de conversão.
Orientação a Objeto
O modelo de orientação a objeto tem as seguintes características básicas:
- Herança simples, com um ancestral comum a todos os objetos chamado “System.Object” O ancestral comum concentra funções de criação, comparação, conversão para string e informações de tipo em tempo de execução.
- Embora a herança seja simples, as classes podem implementar várias “interfaces”. Isto traz as vantagens da herança múltipla sem muitos de seus problemas. Uma interface funciona como se fosse uma “classe abstrata”, que possui apenas protótipos de métodos, sem nenhuma implementação.
- Podemos declarar “properties”, que funcionam sintaticamente como campos, mas na verdade chamam um par de métodos para atribuir ou receber o valor da “property”. As propriedades podem ser também “indexadas” com um inteiro, funcionando como se fossem “arrays” ou indexadas com uma “string”, quando passam a funcionar como um dicionário. O ambiente de desenvolvimento sabe criar “editores de propriedades” para alterar seus valores em tempo de desenvolvimento.
- Os métodos não são a princípio virtuais e devem ser explicitamente declarados como tais com a palavra reservada “virtual”. Existe um protocolo específico para indicar se um método de classe derivada reimplementa um método virtual (override) ou o torna não-virtual (new).
- Podemos declarar um tipo que é um “ponteiro para método”, chamado “delegate”. Um “delegate” contém, a princípio, o endereço da função e também do método que a implementa. Todos os eventos, tão importantes para o funcionamento do ambiente de desenvolvimento, são “delegates”. Os delegates permitem que uma classe chame métodos em outras sem exigir que esta outra classe seja derivada de um ancestral conhecido.
- As informações de tipos em tempo de execução (“reflections”) permitem coisas que normalmente as linguagens compiladas não são capazes como: criar um objeto de uma classe dado seu nome, atualizar propriedades dados seu nome e valor e chamar métodos dados seu nome e argumentos. Tanto o ambiente de desenvolvimento como de execução confiam pesadamente neste mecanismo para funcionarem.
- Podemos atribuir “atributos” a classes e métodos. Os atributos funcionam mais ou menos como uma diretiva de compilação, mas são resolvidos em tempo de execução. Podemos criar novos atributos.
- Existe um mecanismo para herança de formulários.
Veja o “Hello World” em C# para um aplicativo em modo console:
Listagem 1: Hello World em console
public class Class1 { public static int Main(string[] args) { System.Console.WriteLine("Alo, Mundo\n"); return 0; } }
Sem ponteiros
Não existem ponteiros na nova plataforma. Isto não quer dizer que não temos a eficiência dos ponteiros: muitos objetos são tratados por referência. As referências são “ponteiros domesticados”: embora internamente elas sejam ponteiros, elas não podem apontar para locais arbitrários de memória.
A memória não precisa ser liberada pelo programador. Um “garbage collector” - “coletor de lixo” faz o serviço. Isto evita uma série de erros como “vazamentos de memória” e uso de uma variável cuja memória já foi liberada.
Boxing
Os objetos oferecem um modelo muito conveniente para lidar com elementos em nossos programas através da abstração proporcionada por propriedades, métodos, eventos e do mecanismo de herança. O problema é que os objetos têm o custo adicional ao serem sempre acessados através de ponteiros (“this”, “self”) e terem que ser criados e destruídos.
Este custo é irrelevante quando estamos lidando com um objeto complexo e pesado como um formulário na tela ou um arquivo em disco. Mas é um custo muito caro para tipos simples como um inteiro, especialmente visto que a CPU consegue lidar com inteiros de maneira muito eficiente.
A plataforma resolve este problema de uma maneira brilhante: existem duas categorias de tipos: por valor e por referência. Os tipos por valor podem ser automaticamente convertidos para referências através de um processo chamado “boxing”. Isto permite tratar os tipos intrínsecos como se eles tivessem propriedades e métodos, como por exemplo:
Listagem 2: Exemplo de utilização do boxing
int x = 10; string s = x.ToString();
O C# é um C++ “limpo”, com várias boas idéias comuns em outras linguagens e algumas novas, como “boxing”, “delegates”, “garbage collection” e “attributes”. Ela é muito atraente para programadores que desejam migrar para a plataforma “.NET” da Microsoft.