Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
La corrección de errores y errores en el código puede ser una tarea lenta y a veces frustrante. Se tarda tiempo en aprender a depurar de forma eficaz. Un IDE eficaz como Visual Studio puede facilitar mucho el trabajo. Un IDE puede ayudarle a corregir errores y depurar el código más rápidamente y ayudarle a escribir código mejor con menos errores. En este artículo se proporciona una vista holística del proceso de "corrección de errores", por lo que puede saber cuándo usar el analizador de código, cuándo usar el depurador, cómo corregir excepciones y cómo codificar para la intención. Si ya sabe que necesita usar el depurador, consulte Una primera mirada al depurador.
En este artículo, aprenderá a trabajar con el IDE para que las sesiones de codificación sean más productivas. Nos centramos en varias tareas, como:
Preparación del código para la depuración mediante el analizador de código del IDE
Cómo corregir excepciones (errores en tiempo de ejecución)
Cómo minimizar los errores programando con intención (usando aserciones)
Cuándo usar el depurador
Para demostrar estas tareas, mostramos algunos de los tipos de errores más comunes que podrías encontrar al intentar depurar tus aplicaciones. Aunque el código de ejemplo es C#, la información conceptual suele aplicarse a C++, Visual Basic, JavaScript y otros lenguajes compatibles con Visual Studio (excepto donde se indique). Las capturas de pantalla se encuentran en C#.
Crea una aplicación de ejemplo con algunos fallos y errores
El código siguiente tiene algunos errores que puede corregir mediante el IDE de Visual Studio. Esta aplicación es una aplicación sencilla que simula obtener datos JSON de alguna operación, deserializar los datos en un objeto y actualizar una lista sencilla con los nuevos datos.
Para crear la aplicación, debe tener instalado Visual Studio y el componente de desarrollo de escritorio de .NET instalado.
Si aún no ha instalado Visual Studio, vaya a la página de descargas de Visual Studio para instalarlo gratis.
Si necesita instalar la carga de trabajo pero ya tiene Visual Studio, seleccione Herramientas>Obtener herramientas y características. Se inicia el instalador de Visual Studio. Elija la carga de trabajo Desarrollo de escritorio de .NET y, luego, seleccione Modificar.
Siga estos pasos para crear la aplicación:
Abre Visual Studio. En la ventana de inicio, seleccione Crear un proyecto.
En el cuadro de búsqueda, escriba la consola y, a continuación, una de las opciones aplicación de consola para .NET.
Seleccione Siguiente.
Escriba un nombre de proyecto como Console_Parse_JSON y, a continuación, seleccione Siguiente o Crear, según corresponda.
Seleccione la plataforma de destino recomendada o .NET 8 y, después, elija Crear.
Si no ve la plantilla de proyecto Aplicación de consola para .NET, vaya a Herramientas>Obtener herramientas y características, que abre el Instalador de Visual Studio. Elija la carga de trabajo Desarrollo de escritorio de .NET y, luego, seleccione Modificar.
Visual Studio crea el proyecto de consola, que aparece en el Explorador de soluciones en el panel derecho.
Cuando el proyecto esté listo, reemplace el código predeterminado del archivo Program.cs del proyecto por el código de ejemplo siguiente:
using System;
using System.Collections.Generic;
using System.Runtime.Serialization.Json;
using System.Runtime.Serialization;
using System.IO;
namespace Console_Parse_JSON
{
class Program
{
static void Main(string[] args)
{
var localDB = LoadRecords();
string data = GetJsonData();
User[] users = ReadToObject(data);
UpdateRecords(localDB, users);
for (int i = 0; i < users.Length; i++)
{
List<User> result = localDB.FindAll(delegate (User u) {
return u.lastname == users[i].lastname;
});
foreach (var item in result)
{
Console.WriteLine($"Matching Record, got name={item.firstname}, lastname={item.lastname}, age={item.totalpoints}");
}
}
Console.ReadKey();
}
// Deserialize a JSON stream to a User object.
public static User[] ReadToObject(string json)
{
User deserializedUser = new User();
User[] users = { };
MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(json));
DataContractJsonSerializer ser = new DataContractJsonSerializer(users.GetType());
users = ser.ReadObject(ms) as User[];
ms.Close();
return users;
}
// Simulated operation that returns JSON data.
public static string GetJsonData()
{
string str = "[{ \"points\":4o,\"firstname\":\"Fred\",\"lastname\":\"Smith\"},{\"lastName\":\"Jackson\"}]";
return str;
}
public static List<User> LoadRecords()
{
var db = new List<User> { };
User user1 = new User();
user1.firstname = "Joe";
user1.lastname = "Smith";
user1.totalpoints = 41;
db.Add(user1);
User user2 = new User();
user2.firstname = "Pete";
user2.lastname = "Peterson";
user2.totalpoints = 30;
db.Add(user2);
return db;
}
public static void UpdateRecords(List<User> db, User[] users)
{
bool existingUser = false;
for (int i = 0; i < users.Length; i++)
{
foreach (var item in db)
{
if (item.lastname == users[i].lastname && item.firstname == users[i].firstname)
{
existingUser = true;
item.totalpoints += users[i].points;
}
}
if (existingUser == false)
{
User user = new User();
user.firstname = users[i].firstname;
user.lastname = users[i].lastname;
user.totalpoints = users[i].points;
db.Add(user);
}
}
}
}
[DataContract]
internal class User
{
[DataMember]
internal string firstname;
[DataMember]
internal string lastname;
[DataMember]
// internal double points;
internal string points;
[DataMember]
internal int totalpoints;
}
}
¡Encuentra los subrayados de color rojo y verde!
Antes de intentar iniciar la aplicación de ejemplo y ejecutar el depurador, verifique el código en el editor de código en busca de líneas onduladas rojas y verdes. Representan errores y advertencias identificados por el analizador de código del IDE. Los subrayados ondulados rojos indican errores de compilación que deben corregirse antes de poder ejecutar el código. Las líneas onduladas verdes son advertencias. Aunque a menudo puedes ejecutar la aplicación sin corregir las advertencias, pueden ser un origen de errores y a menudo ahorras tiempo y problemas investigandolos. Estas advertencias y errores también se muestran en la ventana Lista de errores, si prefiere una vista de lista.
En la aplicación de ejemplo, ves varias líneas onduladas rojas que necesitas corregir y una verde que necesitas investigar. Este es el primer error.
Para corregir este error, puede ver otra característica del IDE, representada por el icono de bombilla.
¡Revisa la bombilla!
El primer subrayado ondulado rojo representa un error en tiempo de compilación. Mantenga el puntero sobre él y verá el mensaje The name `Encoding` does not exist in the current context.
Observe que este error muestra un icono de bombilla en la parte inferior izquierda. Junto con el icono de destornillador
, el icono de bombilla
representa acciones rápidas que pueden ayudarle a corregir o refactorizar código insertado. La bombilla representa los problemas que debe corregir. El destornillador es para problemas que elija corregir. Use la primera corrección sugerida para resolver este error y haga clic en using System.Text a la izquierda.
Al seleccionar este elemento, Visual Studio agrega la declaración using System.Text en la parte superior del archivo Program.cs y desaparece el subrayado ondulado rojo. (Cuando no está seguro de los cambios aplicados por una corrección sugerida, elija el vínculo Vista previa de los cambios de la derecha antes de aplicar la corrección).
El error anterior es uno común que normalmente se corrige agregando una nueva using instrucción al código. Hay varios errores comunes y similares a este, como The type or namespace "Name" cannot be found. Estos tipos de errores pueden indicar una referencia de ensamblado que falta (haga clic con el botón derecho en el proyecto, elija Agregar>referencia), un nombre mal escrito o una biblioteca que falta (para C#, haga clic con el botón derecho en el proyecto y elija Administrar paquetes NuGet).
Corrección de los errores y advertencias restantes
Hay algunas marcas onduladas más que revisar en este código. Aquí se observa un error común de conversión de tipos. Al mantener el puntero sobre el subrayado ondulado, puede ver que el código pretende convertir una cadena a un int, lo cual no se admite a menos que agregue código explícito para realizar la conversión.
Dado que el analizador de código no puede adivinar su intención, no hay indicadores para ayudarle esta vez. Para corregir este error, debe conocer la intención del código. En este ejemplo, no es demasiado difícil ver que points debe ser un valor numérico (entero), ya que está intentando agregar points a totalpoints.
Para corregir este error, cambie el points miembro de la User clase de esta manera:
[DataMember]
internal string points;
a esto:
[DataMember]
internal int points;
Las líneas onduladas rojas del editor de código desaparecen.
A continuación, mantenga el puntero sobre el subrayado ondulado verde en la declaración del points miembro de datos. El analizador de código indica que la variable nunca tiene asignado un valor.
Normalmente, esto representa un problema que debe corregirse. Sin embargo, en la aplicación de ejemplo, de hecho almacena datos en la variable points durante el proceso de deserialización y, a continuación, agrega ese valor al miembro de datos totalpoints. En este ejemplo, conoce la intención del código y puede omitir la advertencia de forma segura. Sin embargo, si desea eliminar la advertencia, puede reemplazar el código siguiente:
item.totalpoints = users[i].points;
por este:
item.points = users[i].points;
item.totalpoints += users[i].points;
La marca ondulada verde desaparece.
Corrección de una excepción
Cuando haya corregido todos los subrayados ondulados rojos y resueltos,o al menos investigados, todos los subrayados ondulados verdes, estará listo para iniciar el depurador y ejecutar la aplicación.
Presione F5 (Depurar > Iniciar depuración) o el botón Iniciar Depuración en la barra de herramientas de Depuración.
En este momento, la aplicación de ejemplo produce una SerializationException excepción (un error en tiempo de ejecución). Es decir, la aplicación no puede procesar los datos que intenta serializar. Dado que ha iniciado la aplicación en modo de depuración (depurador asociado), el asistente de excepciones del depurador le lleva directamente al código que produjo la excepción y le proporciona un mensaje de error útil.
El mensaje de error indica que el valor 4o no se puede analizar como un entero. Por lo tanto, en este ejemplo, sabe que los datos son incorrectos: 4o debe ser 40. Sin embargo, si no está en control de los datos en un escenario real (supongamos que lo está obteniendo de un servicio web), ¿qué hace sobre ellos? ¿Cómo arregla esto?
Cuando se alcanza una excepción, debe formular (y responder) un par de preguntas:
¿Esta excepción es solo un error que puede corregir? O bien,
¿Esta excepción es algo que podrían encontrar los usuarios?
Si es el primero, corrija el error. (En la aplicación de ejemplo, debe corregir los datos erróneos). Si es la segunda opción, es posible que tenga que gestionar la excepción en su código mediante un bloque try/catch (veremos otras estrategias posibles en la sección siguiente). En la aplicación de ejemplo, reemplace el código siguiente:
users = ser.ReadObject(ms) as User[];
con este código:
try
{
users = ser.ReadObject(ms) as User[];
}
catch (SerializationException)
{
Console.WriteLine("Give user some info or instructions, if necessary");
// Take appropriate action for your app
}
Un try/catch bloque tiene algún costo de rendimiento, por lo que solo querrá usarlos cuando realmente los necesite, es decir, donde (a) podrían producirse en la versión de lanzamiento de la aplicación, y donde (b) la documentación del método indica que debe comprobar la excepción (suponiendo que la documentación esté completa). En muchos casos, puede controlar una excepción correctamente y el usuario nunca tendrá que saberlo.
Estos son un par de sugerencias importantes para el control de excepciones:
Evite usar un bloque catch vacío, como
catch (Exception) {}, que no realiza las acciones adecuadas para exponer o controlar un error. Un bloque catch vacío o no informativo puede ocultar excepciones y dificultar la depuración del código en lugar de facilitarla.Use el
try/catchbloque alrededor de la función específica que produce la excepción (ReadObject, en la aplicación de ejemplo). Si lo usa alrededor de un fragmento mayor de código, terminará ocultando la ubicación del error. Por ejemplo, no use eltry/catchbloque alrededor de la llamada a la funciónReadToObjectprimaria , que se muestra aquí o no sabrá exactamente dónde se produjo la excepción.// Don't do this try { User[] users = ReadToObject(data); } catch (SerializationException) { }Para las funciones desconocidas que incluyas en tu aplicación, especialmente las funciones que interactúan con datos externos (como una solicitud web), comprueba la documentación para ver qué excepciones es probable que lance la función. Puede ser información crítica para el control de errores adecuado y para depurar la aplicación.
Para la aplicación de ejemplo, corrija en SerializationException el GetJsonData método cambiando 4o a 40.
Sugerencia
Si tiene Copilot, puede obtener asistencia de inteligencia artificial mientras depura excepciones. Solo tiene que buscar el botón Preguntar a Copilot
. Para obtener más información, consulte Depurar con Copilot.
Aclaración de la intención del código mediante assert
Seleccione el botón Reiniciar
en la barra de herramientas de depuración (Ctrl + Mayús + F5). Esto reinicia la aplicación en menos pasos. Verá la salida siguiente en la ventana de la consola.
Puede ver que algo de esta salida no es correcto. Los valores name y lastname del tercer registro están en blanco.
Este es un buen momento para hablar sobre una práctica de programación útil, a menudo poco utilizada, que es usar assert instrucciones en tus funciones. Al agregar el siguiente código, se incluye una comprobación en tiempo de ejecución para asegurarse de que firstname y lastname no son null. Reemplace el código siguiente en el UpdateRecords método :
if (existingUser == false)
{
User user = new User();
user.firstname = users[i].firstname;
user.lastname = users[i].lastname;
por este:
// Also, add a using statement for System.Diagnostics at the start of the file.
Debug.Assert(users[i].firstname != null);
Debug.Assert(users[i].lastname != null);
if (existingUser == false)
{
User user = new User();
user.firstname = users[i].firstname;
user.lastname = users[i].lastname;
Al agregar assert instrucciones como esta a las funciones durante el proceso de desarrollo, puede ayudar a especificar la intención del código. En el ejemplo anterior, especificamos los siguientes elementos:
- Se requiere una cadena válida para el primer nombre.
- Se requiere una cadena válida para el apellido
Al especificar la intención de esta manera, usted refuerza sus requisitos. Este es un método sencillo y práctico que puede usar para exponer errores durante el desarrollo. ( lasassert instrucciones también se usan como elemento principal en las pruebas unitarias).
Seleccione el botón Reiniciar
en la barra de herramientas de depuración (Ctrl + Mayús + F5).
Nota:
El assert código solo está activo en una compilación de depuración.
Al reiniciar, el depurador se detiene en la instrucción assert, ya que la expresión users[i].firstname != null se evalúa a false en lugar de true.
El assert error indica que hay un problema que debe investigar.
assert puede cubrir muchos escenarios en los que no se ve necesariamente una excepción. En este ejemplo, el usuario no ve una excepción y se agrega un valor null como firstname en su lista de registros. Esta condición puede causar problemas más adelante (como se ve en la salida de la consola) y podría ser más difícil de depurar.
Nota:
En escenarios en los que se llama a un método en el null valor, se obtiene un NullReferenceException resultado. Normalmente, quiere evitar el uso de un try/catch bloque para una excepción general, es decir, una excepción que no está vinculada a la función de biblioteca específica. Cualquier objeto puede lanzar un NullReferenceException. Compruebe la documentación de la función de biblioteca si no está seguro.
Durante el proceso de depuración, es conveniente mantener una instrucción determinada assert hasta que sepa que necesita reemplazarla por una corrección de código real. Supongamos que decide que el usuario podría encontrar la excepción en una compilación de lanzamiento de la aplicación. En ese caso, debes refactorizar el código para asegurarte de que la aplicación no produce una excepción grave o produce algún otro error. Por lo tanto, para corregir este código, reemplace el código siguiente:
if (existingUser == false)
{
User user = new User();
con este código:
if (existingUser == false && users[i].firstname != null && users[i].lastname != null)
{
User user = new User();
Con este código, cumple con los requisitos de código y se asegura de que no se agregue un registro con un valor de firstname, lastname o null a los datos.
En este ejemplo, insertamos las dos assert instrucciones dentro de un bucle. Normalmente, al usar assert, es mejor agregar assert instrucciones en el punto de entrada (principio) de una función o método. Actualmente está examinando el método UpdateRecords en la aplicación de ejemplo. En este método, sabrás que tienes problemas si alguno de los argumentos del método es null, así que comprueba ambos con una instrucción assert al inicio de la función.
public static void UpdateRecords(List<User> db, User[] users)
{
Debug.Assert(db != null);
Debug.Assert(users != null);
Para las instrucciones anteriores, la intención es que cargue los datos existentes (db) y recupere datos nuevos (users) antes de actualizar cualquier cosa.
Puede usar assert con cualquier tipo de expresión que se resuelva en true o false. Por ejemplo, podría agregar una assert instrucción como esta.
Debug.Assert(users[0].points > 0);
El código anterior es útil si desea especificar la siguiente intención: se requiere un nuevo valor de punto mayor que cero (0) para actualizar el registro del usuario.
Inspecciona tu código en el depurador
Ok, ahora que ha corregido todo lo crítico que está mal con la aplicación de ejemplo, puede pasar a otras cosas importantes.
Le mostramos el asistente de excepciones del depurador, pero el depurador es una herramienta mucho más eficaz que también le permite realizar otras cosas como recorrer el código e inspeccionar sus variables. Estas funcionalidades más eficaces son útiles en muchos escenarios, especialmente en los siguientes escenarios:
Está intentando aislar un error en tiempo de ejecución en el código, pero no puede hacerlo mediante métodos y herramientas descritos anteriormente.
Quieres validar tu código, es decir, verlo mientras se ejecuta para asegurarte de que se comporta de la manera esperada y que haga lo que deseas.
Es instructivo ver el código mientras se ejecuta. Puede obtener más información sobre el código de esta manera y a menudo puede identificar errores antes de que manifieston síntomas obvios.
Para aprender a usar las características esenciales del depurador, consulte Depuración para principiantes absolutos.
Solucionar problemas de rendimiento
Los errores de otro tipo incluyen código ineficaz que hace que la aplicación se ejecute lentamente o use demasiada memoria. Por lo general, optimizar el rendimiento es algo que haces más adelante en el desarrollo de aplicaciones. Sin embargo, puede experimentar problemas de rendimiento temprano (por ejemplo, verá que parte de la aplicación se está ejecutando lentamente) y es posible que tenga que probar la aplicación con las herramientas de generación de perfiles al principio. Para obtener más información sobre las herramientas de generación de perfiles, como la herramienta uso de CPU y el Analizador de memoria, consulte Primer vistazo a las herramientas de generación de perfiles.
Contenido relacionado
En este artículo, ha aprendido a evitar y corregir muchos errores comunes en el código y cuándo usar el depurador. A continuación, obtenga más información sobre el uso del depurador de Visual Studio para corregir errores.