Ladění technik a nástrojů, které vám pomůžou psát lepší kód

Oprava chyb a chyb v kódu může být časově náročná a někdy frustrující úloha. Efektivní ladění trvá dlouho. Výkonné integrované vývojové prostředí, jako je Visual Studio, vám může usnadnit práci. Integrované vývojové prostředí (IDE) vám pomůže rychleji opravit chyby a ladit kód a pomůže vám psát lepší kód s menším počtem chyb. Tento článek obsahuje ucelené zobrazení procesu "oprava chyb", abyste věděli, kdy použít analyzátor kódu, kdy použít ladicí program, jak opravit výjimky a jak kódovat záměr. Pokud už víte, že potřebujete použít ladicí program, podívejte se na první pohled na ladicí program.

V tomto článku se dozvíte, jak pracovat s integrovaným vývojovém prostředím a zvýšit produktivitu vašich programovacích relací. Dotkneme se několika úkolů, například:

  • Příprava kódu na ladění pomocí analyzátoru kódu integrovaného vývojového prostředí (IDE)

  • Oprava výjimek (chyby za běhu)

  • Jak minimalizovat chyby kódováním záměru (pomocí assertu)

  • Kdy použít ladicí program

Abychom si ukázali tyto úlohy, ukážeme si několik nejběžnějších typů chyb a chyb, se kterými se můžete setkat při pokusu o ladění aplikací. I když je vzorový kód C#, koncepční informace se obvykle vztahují na jazyk C++, Visual Basic, JavaScript a další jazyky podporované sadou Visual Studio (s výjimkou případů, kde je uvedeno). Snímky obrazovky jsou v jazyce C#.

Vytvoření ukázkové aplikace s některými chybami a chybami v ní

Následující kód obsahuje některé chyby, které můžete opravit pomocí integrovaného vývojového prostředí sady Visual Studio. Tato aplikace je jednoduchá aplikace, která simuluje získávání dat JSON z určité operace, deserializaci dat na objekt a aktualizaci jednoduchého seznamu s novými daty.

Abyste mohli aplikaci vytvořit, musíte mít nainstalovanou sadu Visual Studio a nainstalovanou úlohu vývoje desktopových aplikací .NET.

  • Pokud jste visual Studio ještě nenainstalovali, přejděte na stránku pro stažení sady Visual Studio a nainstalujte ji zdarma.

  • Pokud potřebujete nainstalovat úlohu, ale sadu Visual Studio už máte, vyberte Nástroje Získat nástroje>a funkce. Spustí se instalační program pro Visual Studio. Zvolte úlohu vývoje desktopových aplikací .NET a pak zvolte Upravit.

Při vytváření aplikace postupujte takto:

  1. Otevřete sadu Visual Studio. V úvodním okně vyberte Vytvořit nový projekt.

  2. Do vyhledávacího pole zadejte konzolua pak jednu z možností konzolové aplikace pro .NET.

  3. Vyberte Další.

  4. Zadejte název projektu, například Console_Parse_JSON, a podle potřeby vyberte Další nebo Vytvořit.

    Zvolte buď doporučenou cílovou architekturu, nebo .NET 8, a pak zvolte Vytvořit.

    Pokud šablonu projektu Konzolová aplikace pro .NET nevidíte, přejděte do části Nástroje>Získat nástroje a funkce, které otevře Instalační program pro Visual Studio. Zvolte úlohu vývoje desktopových aplikací .NET a pak zvolte Upravit.

    Visual Studio vytvoří projekt konzoly, který se zobrazí v Průzkumník řešení v pravém podokně.

Až bude projekt připravený, nahraďte výchozí kód v souboru Program.cs projektu následujícím vzorovým kódem:

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;
    }
}

Najděte červené a zelené vlnovky!

Než se pokusíte spustit ukázkovou aplikaci a spustit ladicí program, zkontrolujte v editoru kódu červené a zelené vlnovky. Tyto chyby a upozornění identifikované analyzátorem kódu integrovaného vývojového prostředí (IDE) představují chyby a upozornění. Červené vlnovky jsou chyby v době kompilace, které je nutné opravit před spuštěním kódu. Zelené vlnovky jsou upozornění. I když můžete aplikaci často spustit bez opravy upozornění, můžou to být zdroje chyb a často ušetříte čas a problémy tím, že je prošetříte. Tato upozornění a chyby se také zobrazí v okně Seznam chyb, pokud dáváte přednost zobrazení seznamu.

V ukázkové aplikaci uvidíte několik červených vlnovek, které potřebujete opravit, a zelený vlnovkou, kterou potřebujete prozkoumat. Tady je první chyba.

Chyba zobrazená jako červená vlnovka

Pokud chcete tuto chybu opravit, můžete se podívat na jinou funkci integrovaného vývojového prostředí (IDE), kterou představuje ikona žárovky.

Podívejte se na žárovku!

První červená vlnovka představuje chybu v době kompilace. Najeďte myší na ni a zobrazí se zpráva The name `Encoding` does not exist in the current context.

Všimněte si, že tato chyba zobrazuje ikonu žárovky vlevo dole. Spolu s ikonou ikona šroubovákušroubováku představuje ikona ikona žárovky žárovky rychlé akce, které vám můžou pomoct opravit nebo refaktorovat kód vložený. Žárovka představuje problémy, které byste měli opravit. Šroubovák je určen pro problémy, které můžete opravit. Pomocí první navrhované opravy tuto chybu vyřešíte kliknutím na System.Text na levé straně.

Oprava kódu pomocí žárovky

Když vyberete tuto položku, Visual Studio přidá using System.Text příkaz v horní části souboru Program.cs a červená vlnovka zmizí. (Pokud si nejste jisti změnami použitými v navrhované opravě, zvolte tlačítko Náhled odkazu na změny napravo před použitím opravy.)

Předchozí chyba je běžná chyba, kterou obvykle opravíte přidáním nového using příkazu do kódu. Existuje několik běžných podobných chyb, jako The type or namespace "Name" cannot be found. jsou tyto druhy chyb, můžou značit chybějící odkaz na sestavení (klikněte pravým tlačítkem myši na projekt, zvolte Přidat>odkaz), chybně napsaný název nebo chybějící knihovnu, kterou potřebujete přidat (pro C#, klikněte pravým tlačítkem myši na projekt a zvolte Spravovat balíčky NuGet).

Oprava zbývajících chyb a upozornění

V tomto kódu se můžete podívat na několik dalších vlnovek. Tady vidíte běžnou chybu převodu typů. Když najedete myší na vlnovku, uvidíte, že se kód pokouší převést řetězec na int, který není podporovaný, pokud k převodu nepřidáte explicitní kód.

Chyba převodu typů

Protože analyzátor kódu nedokáže odhadnout váš záměr, nejsou k dispozici žádné žárovky, které by vám tentokrát pomohly. Pokud chcete tuto chybu opravit, musíte znát záměr kódu. V tomto příkladu není příliš těžké zjistit, že points by měla být číselná hodnota (celé číslo), protože se pokoušíte přidat points do totalpoints.

Pokud chcete tuto chybu opravit, změňte points člena User třídy z tohoto:

[DataMember]
internal string points;

měli změnit na:

[DataMember]
internal int points;

Červené vlnovky v editoru kódu zmizí.

V dalším kroku najeďte myší na zelenou vlnovku v deklaraci datového členu points . Analyzátor kódu vám řekne, že proměnná nemá nikdy přiřazenou hodnotu.

Zpráva upozornění pro nepřiřazenou proměnnou

Obvykle to představuje problém, který je potřeba opravit. V ukázkové aplikaci ale ve skutečnosti ukládáte data do points proměnné během procesu deserializace a pak tuto hodnotu přidáte do datového členu totalpoints . V tomto příkladu znáte záměr kódu a můžete upozornění bezpečně ignorovat. Pokud ale chcete upozornění odstranit, můžete nahradit následující kód:

item.totalpoints = users[i].points;

tímto kódem:

item.points = users[i].points;
item.totalpoints += users[i].points;

Zelená vlnovka zmizí.

Oprava výjimky

Až opravíte všechny červené vlnovky a vyřešíte - nebo alespoň prošetřili - všechny zelené vlnovky, jste připraveni spustit ladicí program a spustit aplikaci.

Stiskněte klávesu F5 (>Ladění spustit ladění) nebo tlačítko Spuštění ladění Spustit ladění na panelu nástrojů Ladění.

V tomto okamžiku ukázková aplikace vyvolá SerializationException výjimku (chyba za běhu). To znamená, že aplikace se chytá na data, která se pokouší serializovat. Vzhledem k tomu, že jste aplikaci spustili v režimu ladění (připojen ladicí program), pomocník výjimky ladicího programu vás vezme přímo do kódu, který vyvolal výjimku a poskytuje užitečnou chybovou zprávu.

SerializationException nastane

Chybová zpráva vás vyzve k tomu, že hodnotu 4o nelze analyzovat jako celé číslo. V tomto příkladu tedy víte, že data jsou špatná: 4o měla by být 40. Pokud ale nemáte kontrolu nad daty ve skutečném scénáři (řekněme, že je získáváte z webové služby), co s tím děláte? Jak to vyřešíte?

Když dojde k výjimce, musíte položit (a odpovědět) několik otázek:

  • Je tato výjimka jen chyba, kterou můžete opravit? Nebo:

  • Je tato výjimka něco, na co se můžou uživatelé setkat?

Pokud se jedná o první, opravte chybu. (V ukázkové aplikaci je potřeba opravit chybná data.) Pokud je to ten druhý, možná budete muset zpracovat výjimku v kódu pomocí try/catch bloku (podíváme se na další možné strategie v další části). V ukázkové aplikaci nahraďte následující kód:

users = ser.ReadObject(ms) as User[];

tímto kódem:

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
}

Blok try/catch má určité náklady na výkon, takže je budete chtít použít jenom v případě, že je skutečně potřebujete, to znamená, že (a) se můžou vyskytnout ve verzi aplikace a kde (b) dokumentace pro metodu znamená, že byste měli zkontrolovat výjimku (za předpokladu, že je dokumentace dokončená!). V mnoha případech můžete o výjimce pracovat správně a uživatel o ní nikdy nebude muset vědět.

Tady je několik důležitých tipů pro zpracování výjimek:

  • Nepoužívejte prázdný blok catch, například catch (Exception) {}, který neprovádí příslušnou akci pro zveřejnění nebo zpracování chyby. Prázdný nebo neinformativní blok catch může skrýt výjimky a může usnadnit ladění kódu.

  • try/catch Použijte blok kolem konkrétní funkce, která vyvolá výjimku (ReadObjectv ukázkové aplikaci). Pokud ho použijete kolem většího bloku kódu, skryjete umístění chyby. Například nepoužívejte try/catch blok kolem volání nadřazené funkce ReadToObject, která je zde zobrazena, nebo nebudete přesně vědět, kde došlo k výjimce.

    // Don't do this
    try
    {
        User[] users = ReadToObject(data);
    }
    catch (SerializationException)
    {
    }
    
  • Informace o neznámých funkcích, které do aplikace zahrnete, zejména funkcí, které pracují s externími daty (například s webovým požadavkem), najdete v dokumentaci a podívejte se, jaké výjimky funkce pravděpodobně vyvolá. Může to být důležité informace pro správné zpracování chyb a ladění aplikace.

U ukázkové aplikace opravte SerializationException metodu GetJsonData změnou 4o na 40.

Tip

Pokud máte Copilot, můžete získat pomoc s AI při ladění výjimek. Stačí vyhledat tlačítko Zeptat se zkopírovaného objektuSnímek obrazovky s tlačítkem Zeptat se copilotu. Další informace naleznete v tématu Ladění pomocí Copilotu.

Objasnění záměru kódu pomocí assertu

Na panelu nástrojů Ladění vyberte tlačítko RestartovatRestartování aplikace (Ctrl + Shift + F5). Tím se aplikace restartuje v menším počtu kroků. V okně konzoly se zobrazí následující výstup.

Hodnota Null ve výstupu

Něco v tomto výstupu není správné. Hodnoty jména a příjmení třetího záznamu jsou prázdné.

To je vhodná doba, kdy si promluvit o užitečném programovacím postupu, který je často nedostatečně využitý, což je použití assert příkazů ve vašich funkcích. Přidáním následujícího kódu zahrnete kontrolu za běhu, abyste se ujistili, že firstname a lastname ne null. Nahraďte následující kód v UpdateRecords metodě:

if (existingUser == false)
{
    User user = new User();
    user.firstname = users[i].firstname;
    user.lastname = users[i].lastname;

tímto kódem:

// 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;

assert Přidáním podobných příkazů do funkcí během procesu vývoje můžete určit záměr kódu. V předchozím příkladu určíme následující položky:

  • Pro křestní jméno se vyžaduje platný řetězec.
  • Pro příjmení se vyžaduje platný řetězec.

Zadáním záměru tímto způsobem vynucujete své požadavky. Jedná se o jednoduchou a praktickou metodu, kterou můžete použít k zobrazení chyb během vývoje. (assert příkazy se také používají jako hlavní prvek v testech jednotek.)

Na panelu nástrojů Ladění vyberte tlačítko RestartovatRestartování aplikace (Ctrl + Shift + F5).

Poznámka:

Kód assert je aktivní pouze v sestavení ladění.

Při restartování se ladicí program pozastaví na assert příkazu, protože výraz users[i].firstname != null se vyhodnotí jako false místo true.

Assert se překládá na false

Tato assert chyba vám říká, že došlo k problému, který je potřeba prošetřit. assert může zahrnovat řadu scénářů, kdy se nemusí nutně zobrazit výjimka. V tomto příkladu se uživateli nezobrazí výjimka a null hodnota se přidá jako firstname v seznamu záznamů. Tento stav může později způsobit problémy (například ve výstupu konzoly) a může být obtížnější ladit.

Poznámka:

Ve scénářích, ve kterých voláte metodu null pro hodnotu, NullReferenceException výsledek. Obvykle se chcete vyhnout použití try/catch bloku pro obecnou výjimku, tj. výjimku, která není svázaná s konkrétní funkcí knihovny. Jakýkoli objekt může vyvolat NullReferenceException. Pokud si nejste jistí, projděte si dokumentaci k funkci knihovny.

Během procesu ladění je dobré zachovat konkrétní assert příkaz, dokud nevíte, že ho potřebujete nahradit skutečnou opravou kódu. Řekněme, že se rozhodnete, že se uživatel může setkat s výjimkou v buildu vydané verze aplikace. V takovém případě musíte refaktorovat kód, abyste měli jistotu, že vaše aplikace nevyvolá závažnou výjimku nebo způsobí jinou chybu. Chcete-li tento kód opravit, nahraďte následující kód:

if (existingUser == false)
{
    User user = new User();

tímto kódem:

if (existingUser == false && users[i].firstname != null && users[i].lastname != null)
{
    User user = new User();

Pomocí tohoto kódu splníte své požadavky na kód a zajistíte, aby se do dat nepřidá záznam s firstname hodnotou nebo lastname hodnotou null .

V tomto příkladu jsme do smyčky přidali dva assert příkazy. Při použití assertje obvykle nejlepší přidat assert příkazy do vstupního bodu (začátek) funkce nebo metody. Právě se díváte na metodu UpdateRecords v ukázkové aplikaci. V této metodě víte, že máte potíže, pokud je některý z argumentů nullmetody , takže je zkontrolujte oba příkazem assert v vstupním bodě funkce.

public static void UpdateRecords(List<User> db, User[] users)
{
    Debug.Assert(db != null);
    Debug.Assert(users != null);

U předchozích příkazů je vaším záměrem načíst existující data (db) a načíst nová data (users) před aktualizací čehokoli.

Můžete použít assert s jakýmkoli druhem výrazu, který se překládá na true nebo false. Můžete například přidat podobný assert příkaz.

Debug.Assert(users[0].points > 0);

Předchozí kód je užitečný, pokud chcete zadat následující záměr: k aktualizaci záznamu uživatele se vyžaduje nová hodnota bodu větší než nula (0).

Kontrola kódu v ladicím programu

Teď, když jste opravili všechno důležité, co je v ukázkové aplikaci špatně, můžete přejít na další důležité věci.

Ukázali jsme vám pomocníka pro výjimky ladicího programu, ale ladicí program je mnohem výkonnější nástroj, který vám také umožňuje provádět další věci, jako je krokování kódu a kontrola jeho proměnných. Tyto výkonnější funkce jsou užitečné v mnoha scénářích, zejména v následujících scénářích:

  • Pokoušíte se izolovat chybu modulu runtime v kódu, ale nemůžete ji provést pomocí metod a nástrojů, které jsme probrali dříve.

  • Chcete ověřit svůj kód, to znamená sledovat, když se spustí, abyste měli jistotu, že se chová tak, jak očekáváte a děláte to, co chcete.

    Je instruktážní sledovat kód, když běží. Další informace o kódu můžete získat tímto způsobem a často identifikovat chyby dříve, než se projeví jakékoli zjevné příznaky.

Pokud chcete zjistit, jak používat základní funkce ladicího programu, přečtěte si téma Ladění pro absolutní začátečníky.

Vyřešit problémy s výkonem

Chyby jiného druhu zahrnují neefektivní kód, který způsobí, že aplikace běží pomalu nebo používá příliš mnoho paměti. Obecně platí, že optimalizace výkonu je něco, co uděláte později ve vývoji aplikací. Brzy ale můžete narazit na problémy s výkonem (například vidíte, že některá část vaší aplikace je pomalá) a možná budete muset svou aplikaci otestovat pomocí nástrojů pro profilaci. Další informace o nástrojích profilace, jako je nástroj Využití procesoru a Analyzátor paměti, najdete v tématu První seznámení s nástroji pro profilaci.

V tomto článku jste se naučili, jak se vyhnout a opravit mnoho běžných chyb v kódu a kdy použít ladicí program. V dalším kroku se dozvíte více o použití ladicího programu sady Visual Studio k opravě chyb.