Módulo 1 · C# — Capítulo 02

Tipos, Variáveis e Operadores

A distinção que não existe em JavaScript e que muda como você pensa sobre memória em C#: tipos por valor vs. tipos por referência.

1. Tipos por valor vs. tipos por referência

Em JavaScript, praticamente tudo que não é primitivo é um objeto acessado por referência, e você raramente precisa pensar nisso. Em C#, essa distinção é explícita e afeta como uma variável se comporta quando é copiada ou passada para um método.

Tipos por valor (struct)Tipos por referência (class)
Onde vivemStack (normalmente)Heap
Ao atribuir b = aCopia o valor inteiro — dois independentesCopia a referência — os dois apontam para o mesmo objeto
Exemplosint, double, bool, char, struct customizadostring, class customizada, arrays, coleções
Tipo por valor (int) Stack a = 10 b = 10 b = a; b++; → a continua 10 Tipo por referência (class) Stack c → ● d → ● Cliente{Nome} d = c; d.Nome = "X"; → c.Nome também muda

Fig. 1 — Copiar uma variável de tipo valor duplica o dado; copiar uma de tipo referência duplica só o "endereço".

C# vendo a diferença na prática
int a = 10;
int b = a;
b++;
Console.WriteLine(a); // 10 — a não foi afetado

var cliente1 = new Cliente { Nome = "Ana" };
var cliente2 = cliente1;
cliente2.Nome = "Bruno";
Console.WriteLine(cliente1.Nome); // "Bruno" — mesmo objeto!
Atenção

Esse é o erro mais comum de quem vem de linguagens dinâmicas: assumir que toda atribuição "copia". Para tipos por referência, cliente2 = cliente1 não cria um novo cliente — cria uma segunda etiqueta apontando para o mesmo objeto na memória.

2. Tipos primitivos essenciais

TipoTamanhoUso típico
int32 bitsContadores, Ids, quantidades
long64 bitsNúmeros muito grandes (Ids de sistemas distribuídos, timestamps)
double64 bits, ponto flutuanteCálculos científicos — não dinheiro
decimal128 bits, precisão exataDinheiro e qualquer valor financeiro
boolVerdadeiro/falso
char16 bits (UTF-16)Um único caractere
stringreferênciaTexto — imutável, como em JS
decimal vs. double — não é só estilo

double representa números em binário, o que significa que valores decimais "redondos" como 0.1 não têm representação exata — o mesmo problema de arredondamento que existe em 0.1 + 0.2 no JavaScript. decimal usa uma representação de base 10 internamente, exata para valores monetários. Já que este curso trabalha pesado com SQL Server: decimal do C# corresponde exatamente ao tipo DECIMAL/NUMERIC do T-SQL — use os dois juntos sempre que o dado envolver dinheiro.

3. Nullable value types

Tipos por valor normalmente não podem ser null — um int sempre tem algum número. Para representar "ausência de valor" (o mesmo conceito de NULL no SQL Server), C# tem os tipos anuláveis, marcados com ?:

C# int? — o equivalente C# de uma coluna NULL-ável
int? idade = null;          // válido — int normal não aceitaria null
idade = 30;

if (idade.HasValue)
    Console.WriteLine(idade.Value);

// forma mais idiomática
Console.WriteLine(idade ?? 0); // usa 0 se idade for null
Conectando com SQL Server

Uma coluna INT NULL no SQL Server mapeia naturalmente para int? em C#; uma coluna INT NOT NULL mapeia para int puro. Essa correspondência vai reaparecer o tempo todo a partir do módulo de integração com Entity Framework.

4. Operadores essenciais

OperadorNomeExemplo
??Null-coalescingnome ?? "sem nome"
??=Atribuição null-coalescingnome ??= "sem nome"
?.Null-conditionalcliente?.Nome (não lança exceção se cliente for null)
isChecagem/padrão de tipoif (obj is Cliente c)
&& / ||E / OU lógico com curto-circuitoIdêntico ao JS
C# operadores null-safe em cadeia
Cliente? cliente = ObterClienteOuNull();

// sem null-conditional: precisaria de um if aninhado
string cidade = cliente?.Endereco?.Cidade ?? "Cidade não informada";

5. Conversões de tipo

C# distingue conversões implícitas (seguras, sem perda de dado, o compilador faz sozinho) de explícitas (potencialmente perdem dado, exigem um cast escrito por você):

C# implícita vs. explícita vs. parsing
int inteiro = 10;
double implicita = inteiro;       // OK sem cast — int cabe em double sem perda

double valor = 9.99;
int explicita = (int)valor;       // precisa de cast — trunca para 9, perde o .99

string texto = "42";
int convertido = int.Parse(texto);           // lança exceção se o texto não for válido
bool ok = int.TryParse(texto, out int numero); // não lança — devolve true/false
Dica

Prefira sempre TryParse a Parse quando o texto vem de uma fonte não confiável (entrada do usuário, um campo de formulário) — evita que uma entrada inválida derrube a aplicação com uma exceção não tratada.

6. const vs. readonly

constreadonly
Quando o valor é fixadoEm tempo de compilaçãoEm tempo de execução (no construtor, por exemplo)
Pode depender de cálculo/parâmetro?Não — precisa ser um literalSim
Exemploconst double Pi = 3.14159;readonly DateTime CriadoEm = DateTime.Now;

7. Onde isso te leva

Entender valor vs. referência agora evita bugs sutis mais adiante — principalmente quando você começar a passar objetos para métodos (próximo capítulo) e, mais tarde, quando estruturas de dados inteiras trafegarem entre C# e SQL Server.

📌 Resumo do capítulo

  • Tipos por valor (int, struct) copiam o dado inteiro; tipos por referência (class, string) copiam apenas o endereço.
  • Use decimal para dinheiro, nunca double — evita erro de arredondamento binário.
  • int? (nullable) é o equivalente C# de uma coluna SQL que aceita NULL.
  • ??, ??= e ?. lidam com null de forma concisa e segura.
  • Prefira TryParse a Parse para entradas não confiáveis.

✏️ Praticando

  1. Escreva uma classe Cliente e uma struct Ponto (int X, int Y); demonstre a diferença de comportamento ao copiar uma instância de cada.
  2. Escreva um método que receba uma string e devolva um int?, usando TryParse (retornando null se não for possível converter).
  3. Liste três colunas do banco que você criou no Módulo 2 e escreva o tipo C# (incluindo nullable, se aplicável) que cada uma deveria mapear.