image

Access unlimited bootcamps and 650+ courses

50
%OFF

RK

Renata Klaine08/09/2025 11:53
Share

Exceções (Exceptions) - Lidando com o Inesperado

    Em programação, nem tudo sai como planejado. Exceções são a maneira estruturada e robusta que o C# e a plataforma .NET nos dão para lidar com erros e situações inesperadas que ocorrem durante a execução de um programa. Pense em uma exceção como um "sinal de alerta". Quando um problema ocorre (ex: tentar dividir por zero), o sistema dispara esse sinal. Se não houver um plano para "capturar" esse alerta, o programa inteiro para e quebra.

    1. Introdução a Exceções

    Uma exceção é um objeto que representa um erro. Em C#, todas as exceções herdam da classe base System.Exception. Elas contêm informações valiosas sobre o erro, como uma mensagem descritiva (Message) e o "rastro da pilha" (StackTrace), que é um mapa de onde no código o erro ocorreu. O objetivo de usar exceções é separar a lógica de tratamento de erros da lógica principal do programa, tornando o código mais limpo e organizado.

    2. Realizando a Leitura de um Arquivo

    A leitura de arquivos é um exemplo clássico onde exceções são comuns, pois a operação depende de fatores externos ao seu programa:

    • O arquivo pode não existir (FileNotFoundException).
    • O caminho para o arquivo pode estar incorreto (DirectoryNotFoundException).
    • Seu programa pode não ter permissão para ler o arquivo (UnauthorizedAccessException).
    • O arquivo pode estar em uso por outro processo (IOException).

    Exemplo (código que pode quebrar):

    // Se "arquivo_inexistente.txt" não existir, esta linha vai disparar
    // uma FileNotFoundException e o programa irá travar.
    string conteudo = File.ReadAllText("caminho/para/arquivo_inexistente.txt");
    Console.WriteLine(conteudo);
    

    3. Disparando uma Exceção (throw)

    Além de tratar exceções do sistema, você pode criar e "disparar" suas próprias exceções para sinalizar erros específicos da sua regra de negócio. Isso é feito com a palavra-chave throw.

    Informação Adicional: É uma boa prática criar suas próprias classes de exceção personalizadas para erros de negócio (ex: SaldoInsuficienteException), herdando de ApplicationException.

    public void Sacar(decimal valor)
    {
      if (valor > this.Saldo)
      {
          // Dispara uma exceção para sinalizar que a regra de negócio foi violada.
          throw new Exception("Saldo insuficiente para realizar o saque.");
      }
      this.Saldo -= valor;
    }
    

    4. Tratando uma Exceção (try-catch)

    Para evitar que o programa quebre, colocamos o código "perigoso" dentro de um bloco try e o código que trata o erro dentro de um bloco catch.

    try
    {
      // Tenta executar o código que pode gerar uma exceção.
      string conteudo = File.ReadAllText("caminho/para/arquivo_inexistente.txt");
      Console.WriteLine(conteudo);
    }
    catch (Exception ex)
    {
      // Se uma exceção ocorrer no bloco 'try', o código aqui é executado.
      Console.WriteLine($"Ocorreu um erro ao ler o arquivo: {ex.Message}");
    }
    // O programa continua a execução normalmente a partir daqui.
    

    5. Exceção Genérica e Específica

    É possível ter múltiplos blocos catch para tratar diferentes tipos de exceções de maneiras diferentes.

    • Específica: Captura um tipo de erro bem definido (ex: FileNotFoundException). Esta é a melhor prática, pois permite dar um tratamento adequado para cada tipo de erro.
    • Genérica: Captura Exception, a classe base. Funciona como um "pega-tudo" para qualquer erro não tratado pelos blocos catch específicos.

    Informação Adicional: A ordem importa! Os blocos catch devem ser organizados do mais específico para o mais genérico.

    try
    {
      // ... código ...
    }
    catch (FileNotFoundException ex)
    {
      Console.WriteLine("Erro: O arquivo não foi encontrado. Verifique o caminho.");
    }
    catch (UnauthorizedAccessException ex)
    {
      Console.WriteLine("Erro: Sem permissão para ler o arquivo.");
    }
    catch (Exception ex) // Genérico, sempre por último
    {
      Console.WriteLine($"Ocorreu um erro inesperado: {ex.Message}");
    }
    

    6. Entendendo o Bloco finally

    O bloco finally contém um código que será executado sempre, não importa se:

    • O bloco try foi executado com sucesso.
    • Uma exceção foi disparada e capturada por um catch.
    • Uma exceção foi disparada e não foi capturada.

    Seu principal uso é para "limpeza", ou seja, liberar recursos importantes como fechar conexões de banco de dados ou arquivos.

    StreamReader leitor = null;
    try
    {
      leitor = new StreamReader("meu_arquivo.txt");
      // ... lê o arquivo ...
    }
    catch (Exception ex)
    {
      // ... trata o erro ...
    }
    finally
    {
      // Este bloco executa sempre!
      if (leitor != null)
      {
          leitor.Close(); // Garante que o arquivo seja fechado.
      }
    }
    

    Informação Adicional (Melhor Prática): Em C#, para objetos que precisam ser "limpos" (que implementam a interface IDisposable), a instrução using é a forma moderna e preferida de garantir a limpeza, pois ela gera um bloco try...finally por baixo dos panos.

    // Jeito moderno e mais seguro:
    using (StreamReader leitor = new StreamReader("meu_arquivo.txt"))
    {
      // ... usa o leitor ...
    } // O leitor é fechado automaticamente aqui, mesmo se ocorrer um erro.
    

    Exemplo com Try Catch e Finally

    try
    {
      string[] linhas = File.ReadAllLines("arquivos/arquivoLeitura.txt");
    
      foreach (string linha in linhas)
      {
          Console.WriteLine(linha);
      }
    }
    catch (FileNotFoundException ex)
    {
      Console.WriteLine($"Ocorreu um erro na leitura do arquivo. Arquivo não encontrado. {ex.Message}");
    }
    catch (DirectoryNotFoundException ex)
    {
      Console.WriteLine($"Ocorreu um erro na leitura do arquivo. Caminho da pasta não encontrado. {ex.Message}");
    }
    catch (Exception ex)
    {
      Console.WriteLine($"Ocorreu um erro: {ex.Message}");
    }
    finally
    {
      Console.WriteLine("Chegou no bloco finally.");
    }
    

    7. Usando o Throw (dentro de um catch)

    Às vezes, você captura uma exceção, faz algo com ela (como registrar o erro em um log) e depois quer "relançá-la" para que uma camada superior do sistema também possa tratá-la.

    Informação Adicional (Ponto Crítico):

    • throw;: Relança a exceção original, preservando o StackTrace. É a forma correta de se fazer.
    • throw ex;: Lança a exceção como se ela tivesse se originado no bloco catch, destruindo o StackTrace original. Isso dificulta muito a depuração e deve ser evitado.
    try
    {
      // ...
    }
    catch (Exception ex)
    {
      // 1. Loga o erro para análise futura.
      Console.WriteLine("Registrando log do erro...");
    
      // 2. Relança a exceção para a camada superior (forma correta).
      throw;
    }
    

    ---

    Parte 2: Coleções Genéricas - Organizando Seus Dados

    Coleções são classes projetadas para armazenar e gerenciar grupos de objetos de forma eficiente. O C# oferece várias delas no namespace System.Collections.Generic, cada uma com um comportamento e caso de uso específico.

    8. Introdução a Filas e Fila na Prática (Queue<T>)

    Uma Fila (Queue<T>) é uma coleção baseada no princípio FIFO (First-In, First-Out) — o primeiro a entrar é o primeiro a sair.

    • Analogia: Uma fila de banco ou de supermercado.
    • Métodos Principais:Enqueue(item): Adiciona um item no final da fila.
    • Dequeue(): Remove e retorna o item do início da fila.
    • Peek(): Retorna o item do início da fila sem removê-lo.

    Caso de Uso Prático: Processamento de tarefas em ordem de chegada, como uma fila de impressão ou o atendimento de chamados em um sistema de suporte.

    // Fila de senhas para atendimento
    Queue<string> filaAtendimento = new Queue<string>();
    
    // Pessoas chegam e entram no final da fila
    filaAtendimento.Enqueue("Senha-001");
    filaAtendimento.Enqueue("Senha-002");
    filaAtendimento.Enqueue("Senha-003");
    
    // Atendendo na ordem de chegada
    while (filaAtendimento.Count > 0)
    {
      string proximaSenha = filaAtendimento.Dequeue();
      Console.WriteLine($"Chamando para atendimento: {proximaSenha}");
    }
    

    FIFO

    Em programação, FIFO (First-In, First-Out ou "Primeiro a Entrar, Primeiro a Sair") refere-se a uma estrutura de dados, como uma fila, onde o primeiro elemento adicionado é o primeiro a ser removido. É um princípio fundamental em sistemas operacionais para o gerenciamento de processos, em redes para o roteamento de dados e em serviços de impressão para gerenciar trabalhos, garantindo a ordem de processamento.

    9. Introdução a Pilhas e Pilha na Prática (Stack<T>)

    Uma Pilha (Stack<T>) é o oposto da fila. Ela é baseada no princípio LIFO (Last-In, First-Out) — o último a entrar é o primeiro a sair.

    • Analogia: Uma pilha de pratos. Você coloca um prato no topo e pega o prato do topo.
    • Métodos Principais:Push(item): Adiciona um item no topo da pilha.
    • Pop(): Remove e retorna o item do topo da pilha.
    • Peek(): Retorna o item do topo da pilha sem removê-lo.

    Caso de Uso Prático: Histórico de navegação ("voltar"), função "Desfazer" (Undo) em editores de texto.

    // Histórico de páginas visitadas
    Stack<string> historicoNavegador = new Stack<string>();
    
    // Navegando em páginas
    historicoNavegador.Push("google.com");
    historicoNavegador.Push("docs.microsoft.com");
    historicoNavegador.Push("github.com"); // Última página visitada
    
    // Clicando no botão "Voltar"
    Console.WriteLine($"Voltando da página: {historicoNavegador.Pop()}"); // Sai github.com
    Console.WriteLine($"Voltando da página: {historicoNavegador.Pop()}"); // Sai docs.microsoft.com
    

    LIFO

    Em programação, LIFO (do inglês Last-In-First-Out, que significa "o último a entrar, o primeiro a sair") é um princípio de estrutura de dados utilizado em pilhas. O funcionamento é análogo a uma pilha de pratos: o último item adicionado ao topo da pilha é o primeiro a ser retirado, com as operações de inserção chamadas de push e remoção de pop. Essa lógica é aplicada em mecanismos de desfazer/refazer, funções recursivas e na navegação de páginas web.

    10. Introdução ao Dictionary e Removendo/Alterando Elementos

    Um Dicionário (Dictionary<TKey, TValue>) é uma coleção que armazena pares de chave-valor. Cada valor é associado a uma chave única.

    • Analogia: Um dicionário de palavras ou uma agenda de contatos (o nome é a chave, o telefone é o valor).
    • Principal Vantagem: Busca de valores extremamente rápida através da chave.

    Métodos e Operações:

    • Adicionar: meuDicionario.Add(chave, valor);
    • Acessar/Alterar: meuDicionario[chave] = novoValor;
    • Remover: meuDicionario.Remove(chave);
    • Verificar se existe: meuDicionario.ContainsKey(chave);
    Informação Adicional (Melhor Prática): Para obter um valor de forma segura, use TryGetValue. Ele é mais eficiente do que verificar com ContainsKey e depois acessar o valor, pois faz apenas uma busca na coleção.
    Share
    Recommended for you
    Microsoft - Azure AZ-900
    Ri Happy - Front-end do Zero #2
    Avanade - Back-end com .NET e IA
    Comments (0)