Compartilhar via


Tipos (Guia de Programação em C#)

Tipos, variáveis, e valores

C# é uma linguagem fortemente tipada. Cada variável e constante têm um tipo, assim como cada expressão avaliada para um valor. Cada assinatura de método especifica um tipo para cada parâmetro de entrada e para o valor de retorno. A biblioteca de classes do .NET Framework define um conjunto de tipos numéricos internos, bem como tipos mais complexos que representam uma ampla variedade de compilações lógicas, como o sistema de arquivos, as conexões de rede, as coleções e matrizes de objetos e datas. Um programa C# típico usa tipos da biblioteca de classe, bem como os tipos definidos pelo usuário que modelam os conceitos que são específicos do domínio do problema do programa.

As informações armazenadas em um tipo podem incluir o seguinte:

  • O espaço de armazenamento exigido por uma variável do tipo.

  • O máximo e mínimo que podem ser representados.

  • Os membros (métodos, campos, eventos e assim por diante) que ele contém.

  • O tipo base do qual ele herda.

  • A localização da memória para variáveis será alocada em tempo de execução.

  • Os tipos de operações que são permitidas.

O compilador usa informações de tipo para garantir que todas as operações sejam executadas em seu código sejam do tipo seguro. Por exemplo, se você declarar uma variável do tipo int, o compilador permitirá que você use a variável em operações de adição e subtração. Se você tentar realizar essas mesmas operações em uma variável do tipo bool, o compilador irá gerar um erro, conforme mostrado no seguinte exemplo:

int a = 5;             
int b = a + 2; //OK 

bool test = true;

// Error. Operator '+' cannot be applied to operands of type 'int' and 'bool'. 
int c = a + test;

Dica

Desenvolvedores de C e C++, observe que no C#, bool não é conversível em int.

O compilador insere informações do tipo no arquivo executável como metadados. O CLR usa esses metadados em tempo de execução para garantir ainda mais a segurança de tipo de garantia quando aloca e recupera memória.

Especificando tipos em declarações de variável

Ao declarar uma variável ou constante em um programa, você deve especificar seu tipo ou usar a palavra-chave var para permitir o compilador a inferir o tipo. O exemplo a seguir mostra algumas declarações de variáveis que usam tipos numéricos internos e tipos definidos pelo usuário complexos:

// Declaration only: 
float temperature;
string name;
MyClass myClass;

// Declaration with initializers (four examples): 
char firstLetter = 'C';
var limit = 3;
int[] source = { 0, 1, 2, 3, 4, 5 };
var query = from item in source
            where item <= limit
            select item;

Os tipos de parâmetros de método e de valores de retorno são especificados na assinatura do método. A assinatura a seguir mostra um método que exige int como um argumento de entrada e retorna uma cadeia de caracteres:

public string GetName(int ID)
{
    if (ID < names.Length)
        return names[ID];
    else 
        return String.Empty;
}
private string[] names = { "Spencer", "Sally", "Doug" };

Depois que uma variável é declarada, não pode ser declarada novamente com um novo tipo, e não pode ser atribuído um valor que não é compatível com seu tipo declarado. Por exemplo, você não pode declarar um int e depois atribuí-lo um valor Booleano verdadeiro. No entanto, os valores podem ser convertidos em outros tipos, por exemplo, quando eles são atribuídos a novas variáveis ou transmitidos como argumentos do método. Uma conversão de tipos que não causa perda de dados é executada automaticamente pelo compilador. Uma conversão que pode causar perda de dados exige uma conversão no código-fonte.

Para obter mais informações, consulte Conversões cast e conversões de tipo (Guia de Programação em C#).

Tipos internos

C# fornece um conjunto padrão de tipos numéricos internos para representar números inteiros, valores de ponto flutuante, expressões boolianas, caracteres de texto, valores decimais e outros tipos de dados. Também existem tipos string e object internos. Eles estão disponíveis para você usá-los em qualquer programa C#. Para obter mais informações sobre os tipos internos, consulte Tabelas de referência de tipos (Referência de C#).

Tipos personalizados

Você usa as construções struct, class, interface e enum para criar seus próprios tipos personalizados. A própria biblioteca de classes do .NET Framework é uma coleção de tipos personalizados fornecidos pela Microsoft que você pode usar em seus próprios aplicativos. Por padrão, os tipos usados com mais frequência na biblioteca de classe estão disponíveis em qualquer programa C#. Outros ficam disponíveis somente quando você adiciona explicitamente uma referência de projeto ao assembly no qual eles são definidos. Depois que o compilador tiver uma referência ao assembly, você pode declarar variáveis (e constantes) dos tipos declarados nesse assembly no código-fonte. Para obter mais informações, consulte Biblioteca de classes do .NET Framework.

O Sistema de Tipo Comum

É importante compreender dois pontos fundamentais sobre o sistema de tipos em .NET Framework:

  • Suporta o princípio de herança. Os tipos podem derivar de outros tipos, chamados de tipos de base. O tipo derivado herda (com algumas restrições) os métodos, as propriedades e outros membros do tipo base. O tipo base pode derivar por sua vez de outro tipo, nesse caso o tipo derivado herda os membros de ambos os tipos de base na sua hierarquia de herança. Todos os tipos, incluindo numéricos internos, como Int32 (palavra-chave do C#: int), finalmente derivam de um único tipo base, que é Object (palavra-chave do C#: object). Essa hierarquia de tipo unificado é chamada de Common Type System (CTS). Para obter mais informações sobre herança no C#, consulte Herança (Guia de Programação em C#).

  • Cada tipo do CTS é definido como um tipo de valor ou um tipo de referência. Isso inclui todos os tipos personalizados na biblioteca de classes do .NET Framework e também seus próprios tipos definidos pelo usuário. Os tipos que você define usando a palavra-chave estrutura são tipos de valor; todos os tipos numéricos internos são structs. Os tipos que você define usando a palavra-chave classe são tipos de referência. Tipos de referência e tipos de valor têm diferentes regras de tempo de compilação e comportamento de tempo de execução diferente.

A ilustração a seguir mostra a relação entre tipos de valor e tipos de referência no CTS.

Tipos de valor e tipos de referência no CTS

Tipos de valor e referência

Dica

Você pode ver que todos os tipos mais comumente usados são organizados no namespace de System.No entanto, o namespace no qual um tipo está contido não tem nenhuma relação com o fato de ser um tipo de valor ou um tipo de referência.

Tipos de valor

Os tipos de valor derivam de ValueType, que deriva de Object. Os tipos que derivam de ValueType possuem comportamento especial no CLR. As variáveis do tipo de valor contêm seus valores diretamente, o que significa que a memória é alocada internamente no contexto em que a variável é declarada. Não há nenhuma alocação de pilha ou sobrecarga de coleta de lixo separada para variáveis do tipo de valor.

Há duas categorias de tipos de valor: struct e enum.

Os tipos numéricos internos são estruturas, e têm propriedades e métodos que você pode acessar:

// Static method on type Byte.
byte b = Byte.MaxValue;

Mas, você declara e atribui valores a eles como se fossem tipos simples sem agregação:

byte num = 0xA;
int i = 5;
char c = 'Z';

Os tipos de valor são selados, o que significa que, por exemplo, você não pode derivar um tipo de Int32, e você não pode definir uma estrutura para herdar de nenhuma classe ou estrutura definida pelo usuário, pois uma estrutura pode herdar apenas de ValueType. No entanto, uma estrutura pode implementar uma ou mais interfaces. É possível converter um tipo de estrutura para um tipo de interface; isso faz com que uma operação boxing envolve a estrutura dentro de um objeto de tipo de referência na heap gerenciada. Operações de conversão boxing ocorrem quando você passa um tipo de valor para um método que utiliza Object como um parâmetro de entrada. Para obter mais informações, consulte Conversões boxing e unboxing (Guia de Programação em C#).

Você usa a palavra-chave struct para criar seus próprios tipos de valor personalizados. Normalmente, uma estrutura é usada como um contêiner para um pequeno conjunto de variáveis relacionadas, conforme mostrado no exemplo a seguir:

public struct CoOrds
{
    public int x, y;

    public CoOrds(int p1, int p2)
    {
        x = p1;
        y = p2;
    }
}

Para obter mais informações sobre estruturas, consulte Structs (Guia de Programação em C#). Para obter mais informações sobre tipos de valores no .NET Framework, consulte Common Type System.

A outra categoria de tipos de valor é enum. Um enum define um conjunto de constantes integrais nomeadas. Por exemplo, a enumeração FileMode na biblioteca de classes do .NET Framework contém um conjunto de inteiros constantes nomeados que especificam como um arquivo deve ser aberto. É definido conforme mostrado no exemplo a seguir:

public enum FileMode
{
    CreateNew = 1,
    Create = 2,
    Open = 3,
    OpenOrCreate = 4,
    Truncate = 5,
    Append = 6,
}

A constante System.IO.FileMode.Create tem um valor 2. No entanto, o nome é muito mais significativo para os seres humanos que leem o código-fonte e, por esse motivo, é melhor usar enumerações em vez de números literais constantes. Para obter mais informações, consulte FileMode.

Todos os enums herdam de Enum, que herda de ValueType. Todas as regras que se aplicam a struts também se aplicam a enums. Para obter mais informações sobre enums, consulte Tipos de enumeração (Guia de Programação em C#).

Tipos de referência

Um tipo que é definido como classe, representante, matriz ou interface é um tipo de referência. Em tempo de execução, quando você declara uma variável de um tipo de referência, a variável contém o valor null até que você crie explicitamente uma instância do objeto usando o operador new ou atribua a ele um objeto que é criado em outro lugar usando new, as shown in the following example:

MyClass mc = new MyClass();
MyClass mc2 = mc;

Uma interface deve ser inicializada juntamente com um objeto da classe que implementa ele. Se MyClass implementa IMyInterface, você cria uma instância de IMyInterface conforme mostrado no exemplo o seguir:

IMyInterface iface = new MyClass();

Quando o objeto é criado, a memória é alocada no heap gerenciado, e a variável contém apenas uma referência para o local do objeto. Os tipos na heap gerenciada requerem a sobrecarga quando são atribuídos e quando são recuperados pela funcionalidade de gerenciamento automático de memória do CLR, que é conhecido como coleta de lixo. No entanto, a coleta de lixo é também é altamente otimizada e, na maioria dos cenários, não cria um problema de desempenho. Para obter mais informações sobre a coleta de lixo, consulte Gerenciamento automático de memória.

Todas as matrizes são tipos de referência, mesmo se seus elementos são tipos de valor. Matrizes derivam implicitamente da classe Array, mas você as declara e usa com a sintaxe simplificada que é fornecida por C#, conforme mostrado no exemplo o seguir:

// Declare and initialize an array of integers. 
int[] nums = { 1, 2, 3, 4, 5 };

// Access an instance property of System.Array. 
int len = nums.Length;

Tipos de referência oferecem suporte total a herança. Ao criar uma classe, você pode herdar de qualquer outra interface ou classe que não esteja definida como vedada, e outras classes podem herdar da sua classe e substituir os métodos virtuais. Para obter mais informações sobre como criar suas próprias classes, consulte Classes e structs (Guia de Programação em C#). Para obter mais informações sobre herança e métodos virtuais, consulte Herança (Guia de Programação em C#).

Tipos de valores literais

Em C#, os valores literais recebem um tipo do compilador. É possível especificar como um literal numérico deve ser digitado acrescentando uma letra ao final do número. Por exemplo, para especificar que o valor 4,56 deve ser tratado como um float, acrescente um “f” ou “F” após o número: 4.56f. Se nenhuma letra é acrescentada, o compilador inferirá um tipo para o literal. Para obter mais informações sobre quais tipos podem ser especificados com sufixos da letra, consulte as páginas de referência para tipos individuais em Tipos de valor (Referência de C#).

Como os literais são tipados, e todos os tipos derivam de Objectfinalmente, você pode escrever e compilar o código da seguinte forma:

string s = "The answer is " + 5.ToString();
// Outputs: "The answer is 5"
Console.WriteLine(s);

Type type = 12345.GetType();
// Outputs: "System.Int32"
Console.WriteLine(type);

Tipos genéricos

Um tipo pode ser declarado com um ou mais parâmetros de tipo que servem como um espaço reservado para o tipo real (o tipo concreto) que o código do cliente fornecerá quando ele criar uma instância do tipo. Tais tipos são chamados de tipos genéricos. Por exemplo, o tipo .NET Framework List possui um parâmetro de tipo que é determinado por convenção como T. Ao criar uma instância do tipo, você especifica o tipo dos objetos que a lista conterá, por exemplo, cadeia de caracteres:

List<string> strings = new List<string>();

O uso do parâmetro de tipo possibilita reutilizar a mesma classe para conter qualquer tipo de elemento, sem que seja necessário converter cada elemento em objeto. Classes de coleção genérica são chamadas coleções fortemente tipadas porque o compilador sabe o tipo específico de elementos da coleção e pode gerar um erro em tempo de compilação se, por exemplo, você tentar adicionar um inteiro ao objeto strings no exemplo anterior. Para obter mais informações, consulte Genéricos (Guia de Programação em C#).

Tipos implícitos, tipos anônimos e tipos anuláveis

Conforme observado anteriormente, você pode implicitamente digitar uma variável local (mas não membros da classe) usando a palavra-chave var. A variável ainda recebe um tipo em tempo de compilação, mas o tipo é fornecido pelo compilador. Para obter mais informações, consulte Variáveis locais de tipo implícito (Guia de Programação em C#).

Em alguns casos, é inconveniente criar um determinado tipo para conjuntos simples de valores relacionados que você não pretende armazenar ou transferir fora dos limites do método. É possível criar tipos anônimos para essa finalidade. Para obter mais informações, consulte Tipos anônimos (Guia de Programação em C#).

Tipos de valor comuns não podem ter um valor nulo. No entanto, você pode criar tipos de valor anuláveis afixando ? após o tipo. Por exemplo, int? é um tipo de int que pode também ter o valor zero. No CTS, os tipos anuláveis são instâncias do tipo genérico de estrutura Nullable. Os tipos anuláveis são especialmente úteis quando você está passando dados para e de bancos de dados no quais os valores numéricos podem ser nulos. Para obter mais informações, consulte Tipos anuláveis (Guia de Programação em C#).

Seções relacionadas

Para obter mais informações, consulte os seguintes tópicos:

Especificação da Linguagem C#

Para obter mais informações, consulte a Especificação da linguagem C#. A especificação da linguagem é a fonte definitiva para a sintaxe e o uso de C#.

Consulte também

Referência

Tabela de tipos integrais (Referência de C#)

Conceitos

Guia de Programação em C#

Conversão de tipos de dados XML

Outros recursos

Referência de C#