Categorias

Fundamentos de classe do C# – O Sistema de tipos

Fundamentos de classe do C# – O Sistema de tipos

Common Type System (CTS) além de definir os tipos, o CTS também estipula as regras que o Common Language Runtime (CLR) segue com referencia a aplicativos que declaram e utilizam esses tipos.
Veremos neste arquivo esse novo tipo de sistema de tipos para que você possa aprender os tipos disponíveis para os desenvolvedores de C# e entender as ramificações da utilização dos diferentes tipos nos programas C#.

Tudo é um objeto

A maioria das linguagens orientadas a objetos (OO) tem dois tipos diferentes: aqueles tipos que são intrínsecos à linguagem (tipos primitivos) e aqueles tipos que podem ser criados por usuários da linguagem (classes).
Os tipos primitivos são tipos simples como caracteres string e números e classes são tipos criados pelos usuários bem mais elaborados e complexos.
Ter dois conjuntos de tipos discretos causa uma grande quantidade de problemas, onde um deles está diretamente relacionado à compatibilidade. Por exemplo, você deseja ter uma coleção de INTS em um sistema com estes dois conjuntos de tipos, logo você precisará criar uma classe especificamente para armazenar valores do tipo INT e se em dado momento você precisar de uma coleção de DOUBLES, teria que fazer os mesmo para este tipo.
Isto por que estes tipos primitivos geralmente não se têm nada em comum e não são objetos reais, então eles não surgem de uma classe básica comum, isto é eles devem ser tratados individualmente em seus próprios termos.
Outro problema semelhante nestes sistemas tradicionais surge quando você quer que um método possa receber um argumento/parâmetro de qualquer tipo suportado pela linguagem. Como estes tipos primitivos são incompatíveis, não é permitido especificar um argumento como esse a menos que escreva classes empacotadoras para cada tipo primitivo.

Felizmente isto não é mais problema no mundo da .NET e do C# porque tudo no CTS é um objeto.
De fato, não apenas tudo é um objeto como também, ainda mais importante, todos os objetos derivam implicitamente de uma única classe básica definida como parte do CTS.
Essa classe básica, chamada System.Object.

Tipo de valor e tipos de referência

Criar uma linguagem onde tudo é um objeto não é um conceito novo. A maior desvantagem de tornar tudo um objeto sempre foi o desempenho pobre.
Os projetistas do CTS se depararam com a tarefa de criar um sistema de tipos em que tudo é um objeto mas o sistema de tipos trabalha de maneira eficiente quando aplicável.
A solução deles foi separar os tipos de CTS em duas categorias tipos de valor e tipos de referencia.
Esses termos, refletirão como as variáveis são alocadas e como elas funcionam internamente.

Tipos de valor

Quando tiver uma variável que é um tipo de valor, você tem uma variável que contém dados reais. Portanto, a primeira regra de tipos de valor é que eles não podem ser nulos. Por exemplo, abaixo eu aloquei memória criando uma variável do System.Int32 em C#.

int i = 32;

Além disto, o ato de atribuir um valor a i resulta neste valor de 32 bits sendo inserido (moved into) nesse espaço alocado.
Há vários tipos de valor definido em C#, incluindo enumeradores, estruturas e primitivos.
A qualquer hora que declarar uma variável de um desses tipos, você alocou o número de bytes associados com esse tipo na pilha e está trabalhando diretamente com um array alocado de bits. Além disse, ao passar uma variável que é um tipo de valor, você está passando o valor dessa variável e não uma referência a seu objeto subjacente.

Tipos de referência

Os tipos de referências são semelhantes às referências em C++ no sentido de que são ponteiros do tipo seguro isto significa que em vez de ser apenas um endereço que poderia ou não indicar o que você pensa que ele faz, uma referência, quando está não for nula, sempre tem a garantia de apontar para um objeto que é do tipo especificado e que fá foi alocado no heap, além disse temos o fato de uma referência poder ser nula.

Por exemplo, um tipo de referência (string) está sendo alocado, o que está acontecendo nos bastidores é que o valor foi alocado no heap e uma referência a esse valor foi retornada.

string s = "Here Click Software Office";

Como com tipos de valor, existem vários tipos definidos de tipos de referência em c#: classes, arrays, delegados e interfaces.

Boxing e unboxing

Ante o contexto exposto você já deve estar se perguntando como é que essas diferentes categorias de tipos tornam o sistema mais eficiente? Isto é feito por meio do boxing.

Boxing é a conversão de um tipo de valor em um tipo de referência, o caso recíproco é quando um tipo de referência é convertido de volta para um tipo de valor (unboxing).

Essa técnica que faz a .NET ser tão excelente podendo fazer com que tudo seja um objeto, porém só quando realmente necessita ser. Por exemplo, você declara uma variável do tipo System.Int32 a memória para esta pilha é alocada você pode passar esta variável para qualquer método definido como um tipo System.Object e acessa os membros a que esta lhe dá acesso comportando-se como um objeto.

int varNum = 42;		// Tipo de Valor
object varObj = varNum;		// varNum é convertido (boxed) em varObj

Neste exemplo criamos uma variável (varNum) do tipo int. Como já foi dito int é um tipo primitivo, ou seja, é um valor.
Na segunda linha, o compilador vê que a variável varNum esta sendo copiada para um tipo de referencia varObj, fazendo assim o que se é chamado de boxing.

Agora, faremos o contrario converteremos um tipo de referencia para um tipo primitivo, o que recebe o nome de unboxing

int varNum    = 42;		// Tipo de Valor
object varObj       = varNum;	// varNum é convertido (boxed) em varObj
int varNum2  = (int)varObj		//Convertido de volta (unboxed) para int.

A raiz de todos os tipos: system.Object

Suporta todas as classes na hierarquia de classes do .NET Framework e fornece serviços de baixo nível para classes derivadas.
Este é a classe base fundamental de todas as classes do .NET Framework; ela é a raiz da hierarquia de tipos. A tabela 1 descreve os quatro métodos públicos e a tabela 2 os métodos protegidos que todos os tipos obtêm gratuitamente.

Tabela 1 – Métodos públicos do System.Object

  • bool Equals() – Esse método duas referência de objetos em tempo de execução para determinar se elas são exatamente os mesmos objetos. Se as duas variáveis referenciam o mesmo objeto, o valor de retorno é TRUE. No caso de tipos de valor, esse método retorna TRUE se os dois tipos forem idênticos e tiverem o mesmo valor.
  • int GetHashCode() – Recupera o código de hash especificado para um objeto. As funções de hash são utilizadas quando o implementador de uma classe quer colocar o código de hash de um objeto em uma tabela de hash por razões de desempenho.
  • Type GetType() – Utilizado com os métodos de reflexão para recuperar informações de tipo para um dado objeto.
  • string ToString() – Por padrão, esse método é utilizado para recuperar o nome do objeto. Ele pode ser anulado por classes derivadas para retornar uma representação de string do objeto mais amigável ao usuário.
Tabela 2 – Métodos protegidos do System.Object

  • void Finalize() – Esse método é chamado pelo tempo de execução para permitir limpeza antes da coleta de lixo. Observe que esse método pode ou não ser chamado. Portanto, não coloque código que deve executar nesse método. Essa regra entra em algo chamado finalização determinista.

Object MemberwiseClone – Esse membro representa uma cópia rasa (shallow copy) do objeto. Com isso quero dizer uma cópia de um objeto contendo referências a outros objetos que não incluem cópias dos objetos referenciados. Se você precisar que suas classes suportem uma cópia profunda (deep copy), que inclui cópia de objetos referenciados, você mesmo deve programar a interface ICloneable e fazer manualmente a clonagem ou cópia.

Espero ter ajudado de alguma forma. Por gentileza não deixe de postar seus comentários

Até a próxima.