C# e .NET no Console: Datas, Entrada, Lógica e Laços
Guia prático com 12 exercícios comentados para consolidar fundamentos operacionais.
Estas questões não foram montadas para treinar “comandos soltos” de C#. Elas foram organizadas para levar o leitor a um ponto mais importante: sair do nível em que apenas escreve algo no console e chegar ao nível em que constrói programas completos, corretos e verificáveis. Ao longo da lista, ele terá de lidar com manipulação de datas usando DateOnly, DateTime e TimeSpan, entrada de dados com Console.ReadLine(), cálculos numéricos com formatação adequada, decisões com estruturas condicionais, repetição com laços e até controle de estado simples em um jogo com Random.
O primeiro desafio é aprender a respeitar o comportamento real dos dados. Datas, por exemplo, não se comportam como números quaisquer: idade exata, diferença entre datas e próximo aniversário exigem atenção ao calendário, aos anos bissextos e à ideia de intervalo real, não apenas a contas apressadas. Essa parte do material força o leitor a perceber uma distinção central em programação: uma solução pode até “parecer funcionar” em casos fáceis, mas só está correta de fato quando continua funcionando nos casos de borda.
O segundo desafio é compreender que entrada do usuário nunca deve ser tratada como algo trivial. Quando o programa pede nome, idade, temperatura, peso, altura ou salário, ele está pedindo mais do que valores: está exigindo que o aluno transforme texto digitado em informação útil, consistente e pronta para ser processada. Isso desenvolve uma qualidade muito importante em quem programa: parar de confiar cegamente na entrada e passar a pensar em leitura, interpretação, validação e apresentação como partes inseparáveis da solução.
Em seguida, as questões cobram domínio sobre regras de decisão. Verificar se um número é par ou ímpar, classificar uma nota, enquadrar um IMC em faixas e calcular descontos sobre salário parecem tarefas simples à primeira vista, mas todas exigem uma mesma competência profunda: escrever regras sem buracos, sem sobreposições e com limites claros. Aqui o leitor começa a desenvolver raciocínio de negócio, isto é, a capacidade de pegar uma regra verbal e traduzi-la para uma estrutura lógica precisa, coerente e testável.
A parte final acrescenta outro patamar de maturidade: repetição e estado. Na contagem regressiva e na tabuada, o leitor precisa mostrar que entende laços como mecanismo controlado, com início, evolução e parada. No jogo de adivinhação, surge algo ainda mais interessante: além do laço, é necessário manter um estado interno, reagir a palpites sucessivos, comparar valores e fornecer feedback até que a condição de sucesso seja alcançada. Nesse ponto, o aluno deixa de apenas “executar instruções” e começa, de fato, a modelar comportamento de programa.
Sequência Operacional Base
ler -> validar -> transformar -> calcular -> decidir
-> exibir
Nos exercícios com double.TryParse, o parse depende da
cultura numérica da máquina. Se a entrada vier como
1,75 em um ambiente que espera 1.75,
normalize o separador decimal antes do parse ou use cultura
explícita.
using System;
using System.Globalization;
Console.Write("Digite sua data de nascimento (dd/MM/yyyy): ");
string? entrada = Console.ReadLine();
if (!DateOnly.TryParseExact(
entrada,
"dd/MM/yyyy",
CultureInfo.InvariantCulture,
DateTimeStyles.None,
out DateOnly nascimento))
{
Console.WriteLine("Data inválida.");
return;
}
DateOnly hoje = DateOnly.FromDateTime(DateTime.Today);
if (nascimento > hoje)
{
Console.WriteLine("A data de nascimento não pode estar no futuro.");
return;
}
int anos = hoje.Year - nascimento.Year;
if (hoje < nascimento.AddYears(anos))
{
anos--;
}
DateOnly baseData = nascimento.AddYears(anos);
int meses = 0;
while (baseData.AddMonths(1) <= hoje)
{
baseData = baseData.AddMonths(1);
meses++;
}
int dias = hoje.DayNumber - baseData.DayNumber;
Console.WriteLine($"Idade exata: {anos} anos, {meses} meses e {dias} dias.");
O que a questão pede
Conceito cobrado
nascimento, hoje, anos, baseData, meses, dias
Como pensar
Como validar
Tabela de Evidência
| Entrada | Saída esperada |
|---|---|
| Data de nascimento igual a hoje | 0 anos, 0 meses e 0 dias. |
| 29/02 em ano atual não bissexto | A lógica continua válida sem quebrar o calendário. |
| Data futura | Mensagem de data inválida, sem cálculo. |
nascimento: data lida e validada.hoje: referência do calendário atual.anosebaseData: fecham anos completos antes do ajuste fino.mesesedias: completam a decomposição final.
using System;
using System.Globalization;
Console.Write("Digite sua data de nascimento (dd/MM/yyyy): ");
string? entrada = Console.ReadLine();
if (!DateOnly.TryParseExact(
entrada,
"dd/MM/yyyy",
CultureInfo.InvariantCulture,
DateTimeStyles.None,
out DateOnly nascimento))
{
Console.WriteLine("Data inválida.");
return;
}
DateOnly hoje = DateOnly.FromDateTime(DateTime.Today);
DateOnly CriarAniversarioNoAno(int ano, int mes, int dia)
{
int ultimoDia = DateTime.DaysInMonth(ano, mes);
int diaAjustado = Math.Min(dia, ultimoDia);
return new DateOnly(ano, mes, diaAjustado);
}
DateOnly proximoAniversario = CriarAniversarioNoAno(
hoje.Year,
nascimento.Month,
nascimento.Day);
if (proximoAniversario < hoje)
{
proximoAniversario = CriarAniversarioNoAno(
hoje.Year + 1,
nascimento.Month,
nascimento.Day);
}
int diasRestantes = proximoAniversario.DayNumber - hoje.DayNumber;
Console.WriteLine($"Faltam {diasRestantes} dia(s) para o seu próximo aniversário.");
O que a questão pede
Conceito cobrado
hoje, proximoAniversario, diasRestantes
Como pensar
Como validar
Tabela de Evidência
| Entrada | Saída esperada |
|---|---|
| Aniversário hoje | 0 dia(s) restantes. |
| Aniversário já passou no ano atual | A data projetada muda para o próximo ano. |
| Nascimento em 29/02 | O dia é ajustado para o último dia válido de fevereiro. |
nascimento: fornece mês e dia do aniversário.hoje: define o ponto de partida do cálculo.proximoAniversario: data projetada no ano atual ou no próximo.diasRestantes: diferença final em dias.
using System;
using System.Globalization;
Console.Write("Digite a primeira data (dd/MM/yyyy): ");
string? entrada1 = Console.ReadLine();
Console.Write("Digite a segunda data (dd/MM/yyyy): ");
string? entrada2 = Console.ReadLine();
if (!DateTime.TryParseExact(
entrada1,
"dd/MM/yyyy",
CultureInfo.InvariantCulture,
DateTimeStyles.None,
out DateTime data1) ||
!DateTime.TryParseExact(
entrada2,
"dd/MM/yyyy",
CultureInfo.InvariantCulture,
DateTimeStyles.None,
out DateTime data2))
{
Console.WriteLine("Uma ou ambas as datas são inválidas.");
return;
}
if (data1 > data2)
{
(data1, data2) = (data2, data1);
}
TimeSpan intervaloTotal = data2.Date - data1.Date;
int totalDias = intervaloTotal.Days;
int anos = data2.Year - data1.Year;
if (data2.Date < data1.Date.AddYears(anos))
{
anos--;
}
DateTime baseData = data1.Date.AddYears(anos);
int meses = 0;
while (baseData.AddMonths(1) <= data2.Date)
{
baseData = baseData.AddMonths(1);
meses++;
}
int dias = (data2.Date - baseData).Days;
Console.WriteLine("Diferença entre as datas:");
Console.WriteLine($"Total de dias: {totalDias}");
Console.WriteLine($"Intervalo: {anos} ano(s), {meses} mes(es) e {dias} dia(s)");
O que a questão pede
Conceito cobrado
data1, data2, intervaloTotal, totalDias, anos, meses, dias
Como pensar
Como validar
totalDias mede a diferença bruta e
anos/meses/dias mostram a leitura por calendário.
Tabela de Evidência
| Entrada | Saída esperada |
|---|---|
| Mesma data nas duas entradas | Total de dias: 0 e intervalo zerado. |
| Usuário informa primeiro a data maior | O programa reordena internamente e calcula sem erro. |
| 28/02/2024 e 01/03/2024 | O total de dias considera o 29/02 do ano bissexto. |
data1edata2: datas já ordenadas para o cálculo.intervaloTotaletotalDias: diferença bruta em dias.baseData: ponto de apoio para avançar mês a mês.anos,mesesedias: decomposição final.
using System;
Console.Write("Nome: ");
string? nome = Console.ReadLine();
if (string.IsNullOrWhiteSpace(nome))
{
Console.WriteLine("Nome inválido.");
return;
}
Console.Write("Idade: ");
if (!int.TryParse(Console.ReadLine(), out int idade) || idade < 0)
{
Console.WriteLine("Idade inválida.");
return;
}
Console.Write("Telefone: ");
string? telefone = Console.ReadLine();
if (string.IsNullOrWhiteSpace(telefone))
{
Console.WriteLine("Telefone inválido.");
return;
}
Console.Write("E-mail: ");
string? email = Console.ReadLine();
if (string.IsNullOrWhiteSpace(email) || !email.Contains("@"))
{
Console.WriteLine("E-mail inválido.");
return;
}
Console.WriteLine();
Console.WriteLine("=== DADOS CADASTRADOS ===");
Console.WriteLine($"Nome : {nome}");
Console.WriteLine($"Idade : {idade}");
Console.WriteLine($"Telefone : {telefone}");
Console.WriteLine($"E-mail : {email}");
O que a questão pede
Conceito cobrado
nome, idade, telefone, email
Como pensar
Como validar
Tabela de Evidência
| Entrada | Saída esperada |
|---|---|
| Nome, idade, telefone e e-mail válidos | Resumo final com os quatro campos. |
| Nome vazio | Mensagem de nome inválido. |
| Idade negativa ou texto | Mensagem de idade inválida. |
nome: precisa sair de nulo/branco para texto útil.idade: único campo convertido para inteiro.telefoneeemail: permanecem string, mas com validação mínima.
using System;
using System.Globalization;
bool TryReadDouble(string? texto, out double valor)
{
return double.TryParse(
texto,
NumberStyles.Float | NumberStyles.AllowThousands,
new CultureInfo("pt-BR"),
out valor) ||
double.TryParse(
texto,
NumberStyles.Float | NumberStyles.AllowThousands,
CultureInfo.InvariantCulture,
out valor);
}
Console.Write("Digite a temperatura em Celsius: ");
if (!TryReadDouble(Console.ReadLine(), out double celsius))
{
Console.WriteLine("Valor inválido.");
return;
}
double fahrenheit = celsius * 9.0 / 5.0 + 32.0;
double kelvin = celsius + 273.15;
Console.WriteLine($"Fahrenheit: {fahrenheit:F2} °F");
Console.WriteLine($"Kelvin : {kelvin:F2} K");
O que a questão pede
Conceito cobrado
celsius, fahrenheit, kelvin
Como pensar
Como validar
Tabela de Evidência
| Entrada | Saída esperada |
|---|---|
0 |
32.00 °F e 273.15 K. |
100 |
212.00 °F e 373.15 K. |
| Texto inválido | Mensagem de valor inválido. |
celsius: entrada decimal original.fahrenheitekelvin: resultados derivados da mesma leitura.- Em ambiente com cultura diferente, observe o separador decimal aceito por
double.TryParse.
using System;
using System.Globalization;
bool TryReadDouble(string? texto, out double valor)
{
return double.TryParse(
texto,
NumberStyles.Float | NumberStyles.AllowThousands,
new CultureInfo("pt-BR"),
out valor) ||
double.TryParse(
texto,
NumberStyles.Float | NumberStyles.AllowThousands,
CultureInfo.InvariantCulture,
out valor);
}
Console.Write("Digite seu peso em kg: ");
if (!TryReadDouble(Console.ReadLine(), out double peso) || peso <= 0)
{
Console.WriteLine("Peso inválido.");
return;
}
Console.Write("Digite sua altura em metros: ");
if (!TryReadDouble(Console.ReadLine(), out double altura) || altura <= 0)
{
Console.WriteLine("Altura inválida.");
return;
}
double imc = peso / (altura * altura);
string classificacao;
if (imc < 18.5)
classificacao = "Baixo peso";
else if (imc < 25.0)
classificacao = "Peso normal";
else if (imc < 30.0)
classificacao = "Sobrepeso";
else if (imc < 35.0)
classificacao = "Obesidade grau I";
else if (imc < 40.0)
classificacao = "Obesidade grau II";
else
classificacao = "Obesidade grau III";
Console.WriteLine($"IMC: {imc:F2}");
Console.WriteLine($"Classificação: {classificacao}");
O que a questão pede
Conceito cobrado
peso, altura, imc, classificacao
Como pensar
Como validar
Tabela de Evidência
| Entrada | Saída esperada |
|---|---|
70 kg e 1.75 m |
IMC perto de 22.86 e classificação normal. |
Altura 0 |
Mensagem de altura inválida. |
| Entrada negativa | Bloqueio antes do cálculo. |
pesoealtura: entradas decimais a validar.imc: valor calculado antes da classificação.classificacao: resultado textual decidido pelas faixas.
using System;
Console.Write("Digite um número inteiro: ");
if (!int.TryParse(Console.ReadLine(), out int numero))
{
Console.WriteLine("Número inválido.");
return;
}
if (numero % 2 == 0)
Console.WriteLine("O número é par.");
else
Console.WriteLine("O número é ímpar.");
O que a questão pede
Conceito cobrado
numero
Como pensar
Como validar
Tabela de Evidência
| Entrada | Saída esperada |
|---|---|
0 |
Número par. |
-3 |
Número ímpar. |
| Texto inválido | Mensagem de número inválido. |
numero: valor inteiro já validado.numero % 2: prova operacional da decisão.
using System;
using System.Globalization;
bool TryReadDouble(string? texto, out double valor)
{
return double.TryParse(
texto,
NumberStyles.Float | NumberStyles.AllowThousands,
new CultureInfo("pt-BR"),
out valor) ||
double.TryParse(
texto,
NumberStyles.Float | NumberStyles.AllowThousands,
CultureInfo.InvariantCulture,
out valor);
}
Console.Write("Digite uma nota de 0 a 10: ");
if (!TryReadDouble(Console.ReadLine(), out double nota) || nota < 0 || nota > 10)
{
Console.WriteLine("Nota inválida.");
return;
}
string classificacao;
if (nota < 5.0)
classificacao = "Insuficiente";
else if (nota < 7.0)
classificacao = "Regular";
else if (nota < 9.0)
classificacao = "Bom";
else
classificacao = "Excelente";
Console.WriteLine($"Classificação: {classificacao}");
O que a questão pede
Conceito cobrado
nota, classificacao
Como pensar
Como validar
Tabela de Evidência
| Entrada | Saída esperada |
|---|---|
4.9 |
Insuficiente. |
7.0 |
Bom. |
10 |
Excelente. |
nota: entrada decimal a manter entre 0 e 10.classificacao: texto final decidido pelas faixas.- Observe os pontos de fronteira:
5.0,7.0,9.0e10.0.
nota > 5 && nota < 7 e esquecer os valores exatos 5, 7, 9 ou 10. Prefira limites encadeados sem buracos.using System;
using System.Globalization;
bool TryReadDouble(string? texto, out double valor)
{
return double.TryParse(
texto,
NumberStyles.Float | NumberStyles.AllowThousands,
new CultureInfo("pt-BR"),
out valor) ||
double.TryParse(
texto,
NumberStyles.Float | NumberStyles.AllowThousands,
CultureInfo.InvariantCulture,
out valor);
}
Console.Write("Digite o salário bruto: ");
if (!TryReadDouble(Console.ReadLine(), out double salarioBruto) || salarioBruto < 0)
{
Console.WriteLine("Salário inválido.");
return;
}
double aliquota;
if (salarioBruto <= 2000.00)
aliquota = 0.00;
else if (salarioBruto <= 3500.00)
aliquota = 0.075;
else if (salarioBruto <= 5000.00)
aliquota = 0.15;
else
aliquota = 0.225;
double desconto = salarioBruto * aliquota;
double salarioLiquido = salarioBruto - desconto;
Console.WriteLine($"Salário bruto : R$ {salarioBruto:F2}");
Console.WriteLine($"Alíquota : {aliquota * 100:F1}%");
Console.WriteLine($"Desconto : R$ {desconto:F2}");
Console.WriteLine($"Salário líquido: R$ {salarioLiquido:F2}");
O que a questão pede
Conceito cobrado
salarioBruto, aliquota, desconto, salarioLiquido
Como pensar
Como validar
Tabela de Evidência
| Entrada | Saída esperada |
|---|---|
2000.00 |
Alíquota 0% e desconto zerado. |
3500.00 |
Alíquota 7.5%, desconto 262.50 e líquido 3237.50. |
5000.00 |
Alíquota 15% e líquido 4250.00. |
salarioBruto: valor de entrada usado para escolher a faixa.aliquota: percentual aplicado ao cálculo.descontoesalarioLiquido: resultados finais a conferir.
using System;
Console.Write("Digite um número inteiro não negativo: ");
if (!int.TryParse(Console.ReadLine(), out int numero) || numero < 0)
{
Console.WriteLine("Número inválido.");
return;
}
for (int i = numero; i >= 0; i--)
{
Console.Write(i);
if (i > 0)
Console.Write(", ");
}
Console.WriteLine();
O que a questão pede
Conceito cobrado
numero, i
Como pensar
Como validar
numero até 0,
inclusive. O separador só aparece quando ainda há próximo elemento.
Tabela de Evidência
| Entrada | Saída esperada |
|---|---|
3 |
3, 2, 1, 0. |
0 |
Saída contendo apenas 0. |
-1 |
Mensagem de número inválido. |
numero: valor inicial da regressão.i: contador que desce até zero.
using System;
Console.Write("Digite um número inteiro: ");
if (!int.TryParse(Console.ReadLine(), out int numero))
{
Console.WriteLine("Número inválido.");
return;
}
for (int i = 1; i <= 10; i++)
{
Console.WriteLine($"{numero} x {i} = {numero * i}");
}
O que a questão pede
Conceito cobrado
numero, i
Como pensar
i de 1 até 10. O
valor fixo é numero; o valor que muda a cada linha
é i, e o produto forma a saída.
Como validar
Tabela de Evidência
| Entrada | Saída esperada |
|---|---|
5 |
Linhas de 5 x 1 até 5 x 10. |
0 |
Todas as multiplicações resultam em zero. |
| Texto inválido | Mensagem de número inválido. |
numero: fator fixo da tabuada.i: multiplicador que varia de 1 a 10.numero * i: resultado calculado em cada linha.
using System;
Random random = new Random();
int numeroSecreto = random.Next(1, 101); // 1 a 100
int tentativas = 0;
Console.WriteLine("Tente adivinhar o número secreto entre 1 e 100.");
while (true)
{
Console.Write("Seu palpite: ");
if (!int.TryParse(Console.ReadLine(), out int palpite))
{
Console.WriteLine("Digite um número inteiro válido.");
continue;
}
if (palpite < 1 || palpite > 100)
{
Console.WriteLine("O palpite deve estar entre 1 e 100.");
continue;
}
tentativas++;
if (palpite == numeroSecreto)
{
Console.WriteLine($"Parabéns! Você acertou em {tentativas} tentativa(s).");
break;
}
else if (palpite < numeroSecreto)
{
Console.WriteLine("O número secreto é maior.");
}
else
{
Console.WriteLine("O número secreto é menor.");
}
}
O que a questão pede
Conceito cobrado
Random e
tentativas.
numeroSecreto, palpite, tentativas
Como pensar
Como validar
Tabela de Evidência
| Situação | Saída esperada |
|---|---|
| Palpite menor que o número secreto | Mensagem indicando que o número é maior. |
| Palpite maior que o número secreto | Mensagem indicando que o número é menor. |
| Acerto após N tentativas | Mensagem final informando o total de tentativas. |
numeroSecreto: estado oculto sorteado uma única vez.palpite: entrada nova a cada iteração.tentativas: contador cumulativo do jogo.