C# Tipos de Dados

Na aula Variáveis você criou caixas com nome para guardar valores. Mas cada caixa do C# só aceita um tipo de valor: uma guarda números inteiros, outra guarda números com vírgula, outra guarda texto. Esse "tipo" é o tipo de dado. Nesta aula você vai conhecer os tipos que vai usar todos os dias, ver o que cada um guarda e entender por que o compilador reclama quando você tenta pôr um texto numa caixa de número.

Você vai aprender
  • Os seis tipos do dia a dia: int, double, decimal, char, bool e string.
  • Qual tipo escolher para cada informação (idade, altura, preço, letra, sim/não, nome).
  • Quantos bytes cada tipo ocupa, com sizeof.
  • Que int é um apelido de System.Int32, e como descobrir o tipo de um valor com GetType().
  • O valor padrão de cada tipo (default).
  • Os erros que aparecem quando o tipo não combina: CS0029, CS0266 e CS0165.

Todos os programas desta aula foram rodados com o .NET 10 (C# 14) na máquina fictícia da Ana (usuária ana, computador notebook, Ubuntu em português), na pasta ~/cs, com o comando dotnet run arquivo.cs (aula Rodar com dotnet run).


A ideia: potes para cada coisa

Na cozinha da Ana, cada coisa vai num recipiente próprio: o açúcar no pote, o leite na garrafa, os ovos na caixinha. Ninguém põe leite na caixinha de ovos: não cabe, e vazaria.

No C# é igual. Antes de guardar um valor, você diz que tipo de pote a variável é. Esse tipo decide três coisas:

  • o que cabe nela (número inteiro, número com vírgula, texto…);
  • quanto espaço ela ocupa na memória;
  • o que dá para fazer com ela (somar dois números faz sentido; "somar" duas letras, nem tanto).

E o C# confere tudo isso antes de o programa rodar, na compilação. Por isso se diz que o C# é uma linguagem de tipagem estática e forte: o tipo de cada variável é decidido no código e nunca muda.


1. Os seis tipos do dia a dia

A ficha de cadastro da Ana na lojinha tem seis informações, e cada uma pede um tipo diferente:

int idade = 29;
double altura = 1.65;
decimal saldo = 1250.75m;
char inicial = 'A';
bool cadastrada = true;
string nome = "Ana";

Console.WriteLine(nome);
Console.WriteLine(idade);
Console.WriteLine(altura);
Console.WriteLine(saldo);
Console.WriteLine(inicial);
Console.WriteLine(cadastrada);
$ dotnet run tipos.cs
Ana
29
1,65
1250,75
A
True
int idade = 29;
int vem de integer, "inteiro": números sem vírgula, positivos ou negativos. É o tipo padrão para contar coisas: idade, quantidade, pontos. Aula Inteiros.
double altura = 1.65;
double ("duplo", de "precisão dupla"): números com vírgula, para medidas como altura, peso e distância. No código, a vírgula se escreve com ponto: 1.65. Aula float, double e decimal.
decimal saldo = 1250.75m;
decimal: números com vírgula exatos, feitos para dinheiro. O m no fim do número diz "este número é decimal" (de money, "dinheiro"). Sem ele, o C# acha que 1250.75 é double.
char inicial = 'A';
char vem de character, "caractere": um caractere só, entre aspas simples. Aula char e bool.
bool cadastrada = true;
bool (de boolean, "booleano"): só dois valores, true (verdadeiro) e false (falso). Serve para respostas de sim ou não.
string nome = "Ana";
string ("cadeia", de letras): um texto de qualquer tamanho, entre aspas duplas. Aula Strings.

Repare em duas coisas na saída:

  • No código você escreveu 1.65, com ponto, mas a saída mostra 1,65, com vírgula. O código C# é sempre em "inglês" (ponto decimal), mas o Console.WriteLine mostra os números do jeito do país do computador, e o Ubuntu da Ana está em português do Brasil. Isso se chama cultura e tem aula própria: Cultura (pt-BR × en-US).
  • O true virou True, com T maiúsculo. No código é sempre minúsculo; na hora de mostrar, o .NET escreve True e False (e não traduz para "Verdadeiro").

Qual tipo usar para cada informação

A informação é…ExemploTipoValor no código
Uma contagem, sem vírgulaidade, quantidade, pontosint29
Uma medida, com vírgulaaltura, peso, temperaturadouble1.65
Dinheiropreço, saldo, saláriodecimal19.90m
Uma letra ou símboloinicial, sexo na ficha, teclachar'A'
Sim ou nãoestá logado? é maior de idade?booltrue / false
Um textonome, e-mail, endereçostring"Ana"
CPF, CEP e telefone são texto

Eles são feitos de dígitos, mas não são números: ninguém soma dois CEPs, e o zero da frente importa (01310-100). Guarde-os em string: string cep = "01310-100";.

Missão: o programa não compila: a caixa idade é int, mas recebeu um texto ("29", entre aspas). Clique em Rodar para ver o erro CS0029 e depois conserte, guardando o número 29, para mostrar Idade: 29.

int idade = "29";
Console.WriteLine("Idade: " + idade);
int idade = 29;
Console.WriteLine("Idade: " + idade);

O + entre "Idade: " e idade junta o texto com o número (veja Strings; a forma moderna, $"Idade: {idade}", está em Interpolação).


2. Cada tipo ocupa um espaço: sizeof

A memória do computador é medida em bytes. Cada tipo de número ocupa um número fixo deles, e o operador sizeof ("tamanho de") diz quantos:

Console.WriteLine($"bool:    {sizeof(bool)} byte");
Console.WriteLine($"byte:    {sizeof(byte)} byte");
Console.WriteLine($"char:    {sizeof(char)} bytes");
Console.WriteLine($"short:   {sizeof(short)} bytes");
Console.WriteLine($"int:     {sizeof(int)} bytes");
Console.WriteLine($"long:    {sizeof(long)} bytes");
Console.WriteLine($"float:   {sizeof(float)} bytes");
Console.WriteLine($"double:  {sizeof(double)} bytes");
Console.WriteLine($"decimal: {sizeof(decimal)} bytes");
$ dotnet run tamanhos.cs
bool:    1 byte
byte:    1 byte
char:    2 bytes
short:   2 bytes
int:     4 bytes
long:    8 bytes
float:   4 bytes
double:  8 bytes
decimal: 16 bytes
$"… {sizeof(int)} …"
O $ antes das aspas permite pôr um valor dentro do texto, entre chaves. É a interpolação; aqui ela só serve para montar cada linha.
sizeof(int)
Quantos bytes um int ocupa: 4. No C# esses tamanhos são fixos: um int tem 4 bytes no Linux, no Windows, no Mac, no celular e no editor do guia.

Quanto mais bytes, maiores (ou mais precisos) os números que cabem. Um byte vai só de 0 a 255; um long passa de 9 quintilhões. Os tipos byte, short, long e float aparecem nas próximas aulas. O string não está na lista porque não tem tamanho fixo: um texto pode ter 3 letras ou 3 milhões (sizeof(string) nem compila: CS0233, "string não tem um tamanho predefinido").


3. Palavra-chave e nome .NET: int = System.Int32

As palavras int, double, string etc. são apelidos que o C# dá a tipos que vêm do .NET. Cada uma tem um nome "completo". O método GetType() ("obter o tipo") mostra esse nome:

int idade = 29;
double altura = 1.65;
decimal saldo = 1250.75m;
char inicial = 'A';
bool cadastrada = true;
string nome = "Ana";

Console.WriteLine(idade.GetType());
Console.WriteLine(altura.GetType());
Console.WriteLine(saldo.GetType());
Console.WriteLine(inicial.GetType());
Console.WriteLine(cadastrada.GetType());
Console.WriteLine(nome.GetType());
$ dotnet run gettype.cs
System.Int32
System.Double
System.Decimal
System.Char
System.Boolean
System.String

System é o "sobrenome" (o namespace, aula Namespaces e using), e Int32 quer dizer "inteiro de 32 bits" (4 bytes × 8 bits = 32). Escrever int ou Int32 dá exatamente o mesmo tipo:

int a = 10;
System.Int32 b = 20;
Int32 c = 30;
Console.WriteLine(a + b + c);
Console.WriteLine(typeof(int) == typeof(Int32));
$ dotnet run alias.cs
60
True

typeof(int) ("tipo de int") devolve o tipo em si, e o == compara os dois: True, são o mesmo. No dia a dia, use sempre a palavra-chave (int, string): é o que a Microsoft recomenda e o que você vai ver em quase todo código. O nome .NET aparece em mensagens de erro e em métodos como Int32.Parse (igual a int.Parse, aula Texto → Número).

A tabela completa

Palavra-chaveTipo .NETBytesGuarda
boolSystem.Boolean1true ou false
byteSystem.Byte1inteiro de 0 a 255
sbyteSystem.SByte1inteiro de -128 a 127
shortSystem.Int162inteiro de -32 768 a 32 767
ushortSystem.UInt162inteiro de 0 a 65 535
intSystem.Int324inteiro de uns -2,1 a 2,1 bilhões
uintSystem.UInt324inteiro de 0 a uns 4,2 bilhões
longSystem.Int648inteiro de uns -9,2 a 9,2 quintilhões
ulongSystem.UInt648inteiro de 0 a uns 18 quintilhões
floatSystem.Single4número com vírgula, uns 7 dígitos certos
doubleSystem.Double8número com vírgula, uns 15 a 17 dígitos certos
decimalSystem.Decimal16número com vírgula exato, 28 a 29 dígitos
charSystem.Char2um caractere (UTF-16)
stringSystem.Stringvariatexto
objectSystem.Objectvariaqualquer coisa (aula A Classe object)

Os limites exatos de cada tipo estão em Tipos e Limites.


4. Tipos de valor e tipos de referência

Os tipos da tabela se dividem em duas famílias, e por enquanto basta saber os nomes:

  • Tipos de valor: int, double, decimal, char, bool e os outros números. A caixa guarda o próprio valor. Copiar a variável copia o valor.
  • Tipos de referência: string, object, os arrays e as classes que você vai criar. A caixa guarda o endereço de onde o valor está. Uma referência pode valer null ("nada").

A diferença só começa a importar quando você criar seus próprios tipos, e é o assunto de Valor × Referência. O null tem aula própria: null e Tipos Anuláveis.


5. O valor padrão: default

Todo tipo tem um valor padrão, o "zero" daquele tipo. A palavra default mostra qual é:

Console.WriteLine(default(int));
Console.WriteLine(default(double));
Console.WriteLine(default(bool));
Console.WriteLine(default(char) == '\0');
Console.WriteLine(default(string) == null);
$ dotnet run padrao.cs
0
0
False
True
True
default(int) … default(double)
Números valem 0.
default(bool)
false (mostrado como False).
default(char) == '\0'
O char padrão é o caractere de código zero, '\0', que não aparece na tela. Por isso o programa compara em vez de mostrar.
default(string) == null
Tipos de referência valem null.

Esses valores padrão aparecem nos campos de classes e nos arrays (new int[3] nasce com três zeros, aula Arrays). Mas uma variável local, criada dentro do programa, não ganha valor padrão: o C# obriga você a dar um valor antes de usar. Veja em "Quando dá errado", abaixo.


6. O tipo decide o que dá para fazer

Multiplicar um int por um decimal funciona, e o resultado é decimal. Juntar um texto com um número, com +, dá um texto:

int quantidade = 3;
decimal preco = 2.50m;
decimal total = quantidade * preco;
Console.WriteLine(total);
string texto = "Total: " + total;
Console.WriteLine(texto);
$ dotnet run misturar.cs
7,50
Total: 7,50

O int 3 virou decimal sozinho para a conta, porque um inteiro sempre cabe num decimal sem perder nada. Essas conversões automáticas (e as que você precisa pedir) estão em Conversão de Tipos. Repare também que o decimal guarda as casas que você escreveu: 2.50m vezes 3 dá 7,50, com o zero no fim.


Quando dá errado

error CS0029: Não é possível converter implicitamente tipo "string" em "int"
int idade = "29";
Console.WriteLine(idade);
$ dotnet run errotipo.cs
/home/ana/cs/errotipo.cs(1,13): error CS0029: Não é possível converter implicitamente tipo "string" em "int"

Ocorreu uma falha no build. Corrija os erros de build e execute novamente.

O que significa: o valor é de um tipo ("29", entre aspas, é um texto) e a caixa é de outro (int). (1,13) é a linha 1, coluna 13: o lugar do "29". Em inglês, para pesquisar: Cannot implicitly convert type 'string' to 'int'. Como ler essas mensagens: Lendo Erros do Compilador.

Como consertar: tire as aspas (int idade = 29;). Se o número realmente vem num texto (do teclado, de um arquivo), converta com int.Parse ou int.TryParse: aula Texto → Número.

error CS0266: Não é possível converter implicitamente tipo "double" em "int". Existe uma conversão explícita…
int idade = 29;
idade = 29.5;
Console.WriteLine(idade);
$ dotnet run errotipo2.cs
/home/ana/cs/errotipo2.cs(2,9): error CS0266: Não é possível converter implicitamente tipo "double" em "int". Existe uma conversão explícita (há uma conversão ausente?)

Ocorreu uma falha no build. Corrija os erros de build e execute novamente.

O que significa: a variável nasceu int e continua int para sempre. Guardar 29.5 nela perderia o ,5, e o C# não perde dados sem você pedir. "Existe uma conversão explícita" quer dizer: dá para forçar, se você quiser (o cast, (int)29.5, aula Conversão de Tipos).

Como consertar: se a informação pode ter vírgula, a variável tem de ser double (ou decimal) desde o começo: double idade = 29;.

error CS0165: Uso de variável local não atribuída "pontos"
int pontos;
Console.WriteLine(pontos);
$ dotnet run semvalor.cs
/home/ana/cs/semvalor.cs(2,19): error CS0165: Uso de variável local não atribuída "pontos"

Ocorreu uma falha no build. Corrija os erros de build e execute novamente.

O que significa: a caixa pontos foi criada, mas nunca recebeu valor, e o programa tentou ler. Em outras linguagens isso mostraria "lixo" da memória; o C# simplesmente não deixa. Em inglês: Use of unassigned local variable 'pontos'.

Como consertar: dê um valor antes de usar: int pontos = 0;.

O número saiu com vírgula, mas eu escrevi ponto

O que significa: não é erro. O código usa ponto (1.65); a saída usa o jeito do país do computador, e o Ubuntu da Ana está em pt-BR (1,65). Num computador em inglês, o mesmo programa mostraria 1.65.

Como consertar: se você precisa de uma saída igual em qualquer computador (um arquivo para outro programa ler, por exemplo), use CultureInfo.InvariantCulture: aula Cultura.


Referência rápida

TipoParaExemploMostraPadrão
intcontagensint idade = 29;290
doublemedidasdouble altura = 1.65;1,650
decimaldinheirodecimal preco = 19.90m;19,900
charum caracterechar letra = 'A';A'\0'
boolsim/nãobool ativo = true;Truefalse
stringtextostring nome = "Ana";Ananull
sizeof(tipo): bytes do tipo · valor.GetType(): o tipo .NET (System.Int32) · typeof(tipo): o tipo em si · default(tipo): o valor padrão.

Exercícios

1. O tipo do preço

Complete com o tipo certo para guardar o preço de um caderno:

preco = 12.90m;

2. O nome .NET

Qual é o nome .NET (com System.) do tipo int?

3. Quantos bytes?

O que Console.WriteLine(sizeof(long)); mostra?

4. Qual tipo serve para guardar um CEP como 01310-100?

5. O que Console.WriteLine(false); mostra?

6. int x; Console.WriteLine(x); dentro do programa. O que acontece?

Missão final: a ficha do caderno

Missão: a lojinha da Ana cadastrou 12 cadernos de R$ 4,50. Troque cada TIPO pelo tipo certo (um texto, uma contagem, um preço e um sim/não). Enquanto houver um TIPO, o compilador reclama que não conhece esse tipo (CS0246). Quando todos estiverem certos, o programa vai compilar e mostrar Total: 54,00 na última linha.

TIPO produto = "Caderno";
TIPO quantidade = 12;
TIPO preco = 4.50m;
TIPO emEstoque = true;

Console.WriteLine("Produto: " + produto);
Console.WriteLine("Em estoque: " + emEstoque);
Console.WriteLine("Total: " + quantidade * preco);
string produto = "Caderno";
int quantidade = 12;
decimal preco = 4.50m;
bool emEstoque = true;

Console.WriteLine("Produto: " + produto);
Console.WriteLine("Em estoque: " + emEstoque);
Console.WriteLine("Total: " + quantidade * preco);

Na solução, a saída é:

$ dotnet run caderno.cs
Produto: Caderno
Em estoque: True
Total: 54,00

Resumo

  • Toda variável tem um tipo, decidido no código e conferido na compilação. Ele nunca muda.
  • Os seis do dia a dia: int (contagens), double (medidas), decimal (dinheiro, com m), char ('A'), bool (true/false) e string ("Ana").
  • No código, a vírgula é ponto (1.65); na saída em pt-BR, vírgula (1,65). true aparece como True.
  • Os tamanhos são fixos em qualquer computador: int 4 bytes, long 8, double 8, decimal 16, char 2 (sizeof).
  • As palavras-chave são apelidos de tipos .NET: int = System.Int32, string = System.String (GetType()).
  • default: números 0, bool false, referências null. Variáveis locais precisam de valor antes do uso (CS0165).
  • Tipo errado na caixa: CS0029 (não existe conversão) ou CS0266 (existe, mas você precisa pedir).