Buenas prácticas de uso de try-catch en C#
Clase 9 de 14 • Curso de Buenas Prácticas y Código Limpio en C#
Contenido del curso
Clase 9 de 14 • Curso de Buenas Prácticas y Código Limpio en C#
Contenido del curso
Gabriel Mayorga
Bryan Vivas
Jhonatan Alejandro Muñoz Serna
Andres Felipe Zuleta Granados
Leandro Cosme Tomassini
Emilio Nicolás Mendoza Patti
Jaison Mora
Carlos Alberto Irias Torres
Miguel Angel Reyes Moreno
Carlos Alberto Irias Torres
David Elias Gatica Morales
Iván Orlando Ocampo Rivera
Miguel Teheran
Juan Camilo Cadavid Velásquez
Joaquin Grunwald
Juan Pablo Ruiz
Victor Hugo Vázquez Gómez
Luis Sandoval
Brandon Díaz
Santiago Montero
Manuel Cabos
Yezid Garcia Medina
Gilberto Alejandro Monroy Morales
Karen Lisbeth Gelvez Lesmes
Miguel Teheran
Miguel Teheran
Brian Molina
Miguel Teheran
Carlos Andres Segura Pereira
Miguel Teheran
El if para controlar la excepción yo lo invertiría:
if (indexToRemove <= (TaskList.Count - 1) && indexToRemove >= 0)
De esta manera podemos poner las intrucciones para remover la tarea dentro del if y no dentro del else
El try catch quedaría de esta manera:
try { Console.WriteLine("Ingrese el número de la tarea a remover: "); // Show current taks ShowTaskList(); string userInput = Console.ReadLine(); // Remove one position int indexToRemove = Convert.ToInt32(userInput) - 1; if (indexToRemove <= (TaskList.Count - 1) && indexToRemove >= 0) { if (indexToRemove > -1 && TaskList.Count > 0) { string taskToRemove = TaskList[indexToRemove]; TaskList.RemoveAt(indexToRemove); Console.WriteLine("Tarea " + taskToRemove + " eliminada"); } } else { Console.WriteLine("El valor que ha ingresado es invalido"); } } catch (Exception ex) { Console.WriteLine("Ha ocurrido un error al eliminar la tarea"); }
Muy buen aporte amigo
Ha este se le puede aplicar el principio KISS :)
Resumiendo: Se deben controlar todas las posibles excepciones como divisiones por cero, valores fuera de rango etc., y solo dejar al controlador de excepciones ( Try Catch ) un error completamente inesperado.
Usando try-catch en C#
¿Qué es?
try-catch es una estructura en C# que te permite manejar excepciones en tu código.
¿Cómo funciona?
try contiene el código que podría generar una excepción.catch se ejecuta si se produce una excepción en el bloque tryTipos de excepciones:Exception: La clase base para todas las excepciones en C#.Beneficios de usar try-catch:
Consejos para usar try-catch:
try solo para el código que podría generar una excepción.catch para manejar la excepción de forma específica.Message de la excepción para obtener información sobre el error.SystemException: Excepciones que se generan por el sistema operativo o el entorno de .NET.ApplicationException: Excepciones que se generan por la aplicación.try { // Código que podría generar una excepción } catch (Exception ex) { // Manejar la excepción }
En mi caso para evitar poner porciones de codigo en la clausula else y asi tenes una mayor complejidad, opte por utilizar un return;, quedandome el codigo de la siguiente manera:
try { Console.WriteLine("Ingrese el número de la tarea a remover: "); // Show current taks ShowTaskList(); string taskNumberToDeleted = Console.ReadLine(); // Remove one position int indexToRemove = Convert.ToInt32(taskNumberToDeleted) - 1; if (indexToRemove > (TaskList.Count -1) || indexToRemove < 0){ Console.WriteLine("Numero de tarea seleccionado no es valido"); return; } if (indexToRemove > -1 && TaskList.Count > 0) { string taskToRemove = TaskList[indexToRemove]; TaskList.RemoveAt(indexToRemove); Console.WriteLine("Tarea " + taskToRemove + " eliminada"); } } catch (Exception ex) { Console.WriteLine("Ha ocurrido un error al eliminar la tarea"); }
Dos errores posible excepciones encontradas adicionales al video son:
Solución #1:
Solución #2:
Así quedo mi método:
public static void ShowMenuAdd() { try { Console.WriteLine("Ingrese el nombre de la tarea: "); string taskEnteredByUser = Console.ReadLine(); if(string.IsNullOrEmpty(taskEnteredByUser) == true) { Console.WriteLine("Se requiere el nombre de la tarea."); } else { TaskList.Add(taskEnteredByUser); Console.WriteLine("Tarea registrada"); } } catch (Exception) { Console.WriteLine("Ha ocurrido un error al intentar ingresar la tarea."); } }
El método ìsNullOrEmpty ya regresa un booleano y no es necesario poner == true al final.
Queda mejor así:
public static void AddTask() { try { Console.WriteLine("Ingrese el nombre de la tarea: "); string taskToAdd = Console.ReadLine(); if (!string.IsNullOrEmpty(taskToAdd)) { TaskList.Add(taskToAdd); Console.WriteLine("Tarea registrada"); } else { Console.WriteLine("La tarea no puede estar vacía."); } } catch (Exception) { Console.WriteLine("Ha ocurrido un error al intentar ingresar la tarea."); } }
¡Muchas gracias por comentar! Lo de que queda mejor es gusto personal, creo, a mí por ejemplo me gusta leer siempre para lo que se está comparando, se me hace más fácil.
Es importante que al usar un try catch debemos devolver algo para informar al usuario de lo que sucedio, y usarlo es buena practica para el manejo de errores.
Vengo siguiendo la ruta de Backend con C# y .Net propuesta por Platzi, pero veo que acá hay conceptos como el try catch o el sistema de clases, o la programación orientada a objetos en C#, que no se vieron en el curso anterior. Mi pregunta es la siguiente ¿Qué cursos debo tener ya realizados para continuar con esta ruta?
Tendremos cursos complementarios en la Ruta, de momento se estan editando para poder enteder muchas de estas cosas. Este curso definitivamente es para alguien que sepa C# y ya tenga experiencia con el lenguaje.
Holass, yo quité el try catch del todo y manejé todos los casos posibles que se me ocurrieron
public static void ShowMenuRemove() { if (TaskList.Count == 0) { Console.WriteLine("No hay tareas, entonces no se puede remover."); return; } Console.WriteLine("Ingrese el número de la tarea a remover: "); // Show current taks ShowMenuTaskList(); string line = Console.ReadLine(); // Remove one position bool didConvert; int indexToRemove = 0; didConvert = int.TryParse(line, out indexToRemove); if (!didConvert) { Console.WriteLine("Sólo se aceptan números enteros."); return; } if (indexToRemove <= 0) { Console.WriteLine("Sólo se aceptan números mayores que 0."); return; } if (didConvert && indexToRemove > -1 && TaskList.Count > 0) { if (indexToRemove > TaskList.Count) { Console.WriteLine("Número mayor que la cantidad de tareas, no se removió ninguna tarea."); return; } string task = TaskList[indexToRemove - 1]; TaskList.RemoveAt(indexToRemove - 1); Console.WriteLine("Tarea " + task + " eliminada"); } }
Mi solución al segundo try catch:
public static void ShowMenuAdd() { try { Console.WriteLine("Ingrese el nombre de la tarea: "); string task = Console.ReadLine(); if (task.Length < 1) Console.WriteLine("Debe ingresar al menos un caracter"); else { TaskList.Add(task); Console.WriteLine("Tarea registrada"); } } catch (Exception) { Console.WriteLine("No se pudo agregar la tarea"); } }
excelente tu solución me dio una guía, gracias.
Yo le agregaria el metodo Trim a todos los inputs del usuario, ademas de otros metodos para sanitizar el input.
Para el método de remover yo lo haría de la siguiente forma, como sé que el indice debe ser un valor positivo en un inicio declaré la variable como uint y le aplique un tryparse para validar que no ingrese valores negativos o letras, y luego para eliminar le realicé el cast hacia int.
public static void ShowMenuRemove() { try { Console.WriteLine("Ingrese el número de la tarea a remover: "); // Show current taks ShowTaskList(); string numberTaskToRemove = Console.ReadLine(); uint indexToRemove = 0; if (!uint.TryParse(numberTaskToRemove,out indexToRemove) || ((indexToRemove - 1) > (TaskList.Count - 1))) { Console.WriteLine("El valor ingresado no es valido"); return; } int index = (int)indexToRemove - 1; // Remove one position string taskRemoved = TaskList[index]; TaskList.RemoveAt(index); Console.WriteLine("Tarea " + taskRemoved + " eliminada"); } catch (Exception) { Console.WriteLine("Ha ocurrido un error al eliminar la tarea"); } }
Para evitar muchos ELSE, igual desde el primer IF podemos hacer que se termine la ejecución de esa función, agregando un RETURN.
public static void RemoveTask() { try { Console.WriteLine("Ingrese el número de la tarea a remover: "); // Show current taks PrintTasks(); string indexInput = Console.ReadLine(); // Remove one position int indexToRemove = Convert.ToInt32(indexInput) - 1; if (indexToRemove > TaskList.Count - 1) { Console.WriteLine("El número de la tarea está fuera de rango."); return; } if (indexToRemove > -1 && TaskList.Count > 0) { string task = TaskList[indexToRemove]; TaskList.RemoveAt(indexToRemove); Console.WriteLine("Tarea " + task + " eliminada. 👍🏼"); } } catch (Exception ex) { Console.WriteLine("No se pudo eliminar el tarea. Tarea no encontrada."); Console.WriteLine(ex.Message); } }```
Mi codigo para ShowMenuAdd
public static void ShowMenuAdd() { try { Console.WriteLine("Ingrese el nombre de la tarea: "); string taskAdd = Console.ReadLine(); if(string.IsNullOrWhiteSpace(taskAdd)) { System.Console.WriteLine("No se permiten campos nullos"); return; } else { TaskList.Add(taskAdd); Console.WriteLine("Tarea registrada"); } } catch (Exception) { System.Console.WriteLine("Ha surgido un erro al ingresar la tarea"); } }
Identifico que el siguiente try catch es innecesario, lo reemplacé por una condicional
public static void ShowMenuAdd() { Console.WriteLine("Ingrese el nombre de la tarea: "); string task = Console.ReadLine(); if (string.IsNullOrWhiteSpace(task)) { Console.WriteLine("La tarea no puede estar vacía."); return; } TaskList.Add(task); Console.WriteLine("Tarea registrada"); }
Es mejor validar con string.IsNullOrWhiteSpace(taskNew) que con string.IsNullOrEmpty(taskNew) , dado que estamos considerando espacios o caracteres vacios.
Aquí mi reto, revisando si ya existe o si no tiene contenido el string.
public static void ShowMenuAdd() { try { Console.WriteLine("Ingrese el nombre de la tarea: "); string newTask = Console.ReadLine(); if (string.IsNullOrWhiteSpace(newTask)) { Console.WriteLine("No se puede agregar una tarea vacía."); return; } bool exist = TaskList.Any(t => t.Trim().ToLower() == newTask.ToLower()); if (exist) { Console.WriteLine($"La tarea {newTask} ya existe."); } else { TaskList.Add(newTask); Console.WriteLine("Tarea registrada"); } } catch (Exception ex) { Console.WriteLine("Ha Ocurrido uin error al añadir la tarea."); } }
teniendo en cuanta la clase anterior donde corregimos un error de condicionales, ¿no seria mejor aplicar una solución como esta ? ya que evitamos realizar condicionales que pueden ser innecesarios, por favor me corrigen si estoy equivocado y si hay alguna razón por la que se realiza de la manera como se hizo en el vídeo: int indexToRemove = Convert.ToInt32(ReadTaskDeleted) - 1; if (indexToRemove > -1 && TaskList.Count > 0 && indexToRemove<=(TaskList.Count-1)) { string task = TaskList[indexToRemove]; TaskList.RemoveAt(indexToRemove); Console.WriteLine("Tarea " + task + " eliminada");
} else { Console.WriteLine("Debe ingresar un numero correcto para eliminar la tarea"); }
Hola Karen esta solución es valida pero debes tener en cuenta no mas el escenario en que el usuario puede ingresar algo que no es un numero sino una letra o caracter especial.
Claro es una solución valida, no mas debes tener en cuenta que esto no serviría si el usuario ingresa un carácter especial o una letra. Existen muchas maneras de llegar a lo mismo, lo ideal es simplificar y seguir las mejores practicas no importa cómo se implemente el código.
Se podría usar un **else if ** para no anidar el **if **dentro del else?
Es una opción muy valida también, al final todo depende del escenario. ingresar un if dentro de un else seria en el escenario donde puedes tener varias condiciones y adicional un código que siempre se ejecuta, haciendolo con else if te tocaría agregar cada escenario repitiendo el código que iría solo una vez en el else. Nuevamente todo dependería del escenario por eso las buenas prácticas no hay un debe ser, todo va depender del escenario.
Se puede decir que es recomendable controlar y manejar todas las posibles excepciones y solo dejar al controlador de excepciones (try-catch) el manejo de errores completamente inesperados?
Correcto, esa seria al conclusion mas acertada