Przejdź do treści stopki
Iron Academy Logo
Naucz się C#
Naucz się C#

Inne Kategorie

Obsługa wyjątków w C#

Tim Corey
59m 46s

Obsluga wyjątków jest kluczowym aspektem rozwoju odpornych aplikacji. Video Tima Coreya na temat "Obsluga wyjątków w C# - Kiedy je zlapac, gdzie je zlapac i jak je zlapac" dostarcza szczegółowego wyjasnienia, czym są wyjątki, jak je obsługiwac i gdzie je obsługiwac.

Niniejszy artykuł ma na celu wyjaśnienie obsługi wyjątków w C# za pomoca video Tima Coreya. Jest to potężna funkcja pozwalająca programistom zarządzać błędami i wyjątkowymi warunkami, które pojawiają się podczas wykonania programu. Używając bloków try, catch i finally, C# dostarcza strukturalny sposób na obsługę błędów w czasie wykonywania, logowanie wyjątków i utrzymanie przepływu programu.

Wprowadzenie

Tim zaczyna od wyjaśnienia, że wielu programistów ma błędne spojrzenie na wyjątki i ich obsługę. Podkreśla znaczenie zrozumieniuiuiuiuia, czym są wyjątki i gdzie oraz jak je prawidłowo obsługiwać, aby tworzyć bardziej odporne aplikacje.

Budowanie demonstracyjnej aplikacji konsolowej

Tim tworzy aplikację konsolową w Visual Studio 2017, aby zademonstrować obsługę wyjątków. Zaleca używanie aplikacji konsolowych do testowania nowych tematów, ponieważ wymagają minimalnego przygotowania i są łatwe w obsłudze.

using System;

namespace ExceptionsDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            // Placeholder for input and output operations
            Console.ReadLine();
        }
    }
}
using System;

namespace ExceptionsDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            // Placeholder for input and output operations
            Console.ReadLine();
        }
    }
}

Tworzenie biblioteki klas

Tim dodaje bibliotekę klas do rozwiązania, aby zasymulować scenariusz rzeczywisty, w którym różne metody wywołują się nawzajem.

Csharp Exception Handling 1 related to Tworzenie biblioteki klas

Usuwa domyślną klasę i tworzy nową klasę o nazwie DemoCode.

public class DemoCode
{
    // Method to retrieve a number based on the provided position
    public int GetNumber(int position)
    {
        int[] numbers = { 1, 4, 7, 2 };
        return numbers[position];
    }

    // Intermediate method calls GetNumber
    public int ParentMethod(int position)
    {
        return GetNumber(position);
    }

    // Top-level method calls ParentMethod
    public int GrandparentMethod(int position)
    {
        return ParentMethod(position);
    }
}
public class DemoCode
{
    // Method to retrieve a number based on the provided position
    public int GetNumber(int position)
    {
        int[] numbers = { 1, 4, 7, 2 };
        return numbers[position];
    }

    // Intermediate method calls GetNumber
    public int ParentMethod(int position)
    {
        return GetNumber(position);
    }

    // Top-level method calls ParentMethod
    public int GrandparentMethod(int position)
    {
        return ParentMethod(position);
    }
}

Klasa DemoCode zawiera metody, które wywołują się nawzajem, ostatecznie pobierając liczbę z tablicy na podstawie podanej pozycji.

Symulacja wyjątku

Tim wyjaśnia, że aplikacja ma na celu demostrowanie porażek zamiast sukcesów. Wprowadza wyjątek wyjścia poza zakres, przekazując nieprawidłową pozycję do metody GrandparentMethod.

DemoCode demo = new DemoCode();
int result = demo.GrandparentMethod(4); // This will cause an IndexOutOfRangeException
Console.WriteLine($"The value at the given position is {result}");
DemoCode demo = new DemoCode();
int result = demo.GrandparentMethod(4); // This will cause an IndexOutOfRangeException
Console.WriteLine($"The value at the given position is {result}");

Uruchomienie powyższego kodu z nieprawidłową pozycją skutkuje wyjątkiem IndexOutOfRangeException. Tim pokazuje, jak debugger Visual Studio podświetla problem i dostarcza szczegółowych informacji o wyjątku.

Jak NIE używać try-catch

Tim wyjaśnia powszechny błąd, który popełniają programiści, gdy pierwszy raz uczą się bloków try-catch. Często obejmują cały blok kodu, w którym spodziewają się wyjątku, co może prowadzić do niewłaściwej obsługi.

try
{
    int output = 0;
    output = numbers[position];
    return output;
}
catch (Exception ex)
{
    // Avoid returning default values that can mask the problem
    return 0;
}
try
{
    int output = 0;
    output = numbers[position];
    return output;
}
catch (Exception ex)
{
    // Avoid returning default values that can mask the problem
    return 0;
}

Tim podkreśla, że to podejście jest problematyczne, ponieważ ukrywa wyjątek i kontynuuje wykonywanie kodu z nieprawidłowymi założeniami. Na przykład zwracanie 0 jako wartości domyślnej może nie być odpowiednie i może powodować dalsze problemy.

Prawidłowa obsługa wyjątków

Tim podkreśla, że wyjątki dostarczają istotnych informacji o nieoczekiwanych stanach w aplikacji. Jeśli aplikacja kontynuuje w takim stanie bez właściwej obsługi, może to prowadzić do dalszych błędów i korupcji danych.

Zamiast połykania wyjątków, istotne jest, aby obchodzić się z nimi odpowiednio. Oto lepsze podejście:

try
{
    return numbers[position];
}
catch (Exception ex)
{
    // Log the exception or handle it appropriately
    Console.WriteLine(ex.Message);
    throw; // Re-throw the exception to be handled by a higher-level handler
}
try
{
    return numbers[position];
}
catch (Exception ex)
{
    // Log the exception or handle it appropriately
    Console.WriteLine(ex.Message);
    throw; // Re-throw the exception to be handled by a higher-level handler
}

Przez ponowne wyrzucenie wyjątku, upewniasz się, że problem jest propagowany i może być obsługiwany na wyższym poziomie, jeśli jest to konieczne.

Dostarczanie użytecznych informacji użytkownikowi

Tim wyjaśnia, że podczas gdy niektóre wyjątki można obsłużyć bezpiecznie bez zawieszania aplikacji, ważne jest, aby dostarczać przydatne informacje zwrotne użytkownikowi. Na przykład, pokazując okno z komunikatem lub powiadomienie z opcją ponowienia operacji.

Bardziej przydatne informacje: StackTrace

Tim demonstruje, jak używać właściwości StackTrace obiektu wyjątku, aby uzyskać szczegółowe informacje o miejscu wystąpienia wyjątku. Obejmuje to klasę, metodę i numer linii, co jest nieocenione podczas debugowania.

try
{
    return numbers[position];
}
catch (Exception ex)
{
    Console.WriteLine(ex.StackTrace);
    throw;
}
try
{
    return numbers[position];
}
catch (Exception ex)
{
    Console.WriteLine(ex.StackTrace);
    throw;
}

Właściwość StackTrace zapewnia pełny ślad wywołań stosu, pomagając programistom zlokalizować dokładne miejsce problemu.

Prawidłowe umiejscawianie bloków try-catch

Tim wyjaśnia, że właściwa obsługa wyjątków polega nie tylko na ich łapaniu, ale również na wiedzy, gdzie umieścić bloki try-catch. Kluczowym jest umieszczenie bloków try-catch na poziomie, gdzie mamy wystarczający kontekst, aby odpowiednio obsłużyć wyjątek.

Przykład niewłaściwego umiejscowienia

Umieszczenie bloku try-catch głęboko w stosie wywołań często nie pozwala na skuteczne obsłużenie wyjątku, ponieważ brakuje kontekstu operacji wyższego poziomu.

// Deep level exception handling (not ideal)
try
{
    return numbers[position];
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
    throw;
}
// Deep level exception handling (not ideal)
try
{
    return numbers[position];
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
    throw;
}

Przykład właściwego umiejscowienia

Umieszczenie bloku try-catch na najwyższym poziomie, takiego jak w interfejsie użytkownika czy punkcie wejścia do aplikacji, pozwala obsłużyć wyjątki z pełnym kontekstem operacji.

try
{
    int result = demo.GrandparentMethod(4); // This will cause an IndexOutOfRangeException
    Console.WriteLine($"The value at the given position is {result}");
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
    Console.WriteLine(ex.StackTrace);
}
try
{
    int result = demo.GrandparentMethod(4); // This will cause an IndexOutOfRangeException
    Console.WriteLine($"The value at the given position is {result}");
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
    Console.WriteLine(ex.StackTrace);
}

W ten sposób można dostarczyć bardziej informacyjne komunikaty użytkownikowi i zdecydować, czy aplikacja może kontynuować działanie, czy powinna zostać zakończona.

Informacja o śladzie stosu

Tim podkreśla znaczenie informacji o śladzie stosu w diagnozowaniu wyjątków. Ślad stosu dostarcza szczegółowej historii wywołań, pokazując, gdzie wystąpił wyjątek i łańcuch wywołań metod, które do niego doprowadziły.

try
{
    int result = demo.GrandparentMethod(4); // This will cause an IndexOutOfRangeException
    Console.WriteLine($"The value at the given position is {result}");
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
    Console.WriteLine(ex.StackTrace);
}
try
{
    int result = demo.GrandparentMethod(4); // This will cause an IndexOutOfRangeException
    Console.WriteLine($"The value at the given position is {result}");
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
    Console.WriteLine(ex.StackTrace);
}

Ten wynik daje dokładną lokalizację wyjątku i ścieżkę przejścia przez kod, co ułatwia debugowanie i naprawę problemu.

Demonstracja obsługi logiki

Tim demonstruje, jak obsługiwać logikę na odpowiednim poziomie. Na przykład, jeśli metoda jest odpowiedziąlna za otwieranie i zamykanie połączenia z bazą danych, powinna obsługiwać wyjątki, aby upewnić się, że zasoby są prawidłowo zarządzane.

public int GrandparentMethod(int position)
{
    try
    {
        Console.WriteLine("Open database connection");
        int output = ParentMethod(position);
        Console.WriteLine("Close database connection");
        return output;
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
        throw; // Ensure the exception is propagated
    }
}
public int GrandparentMethod(int position)
{
    try
    {
        Console.WriteLine("Open database connection");
        int output = ParentMethod(position);
        Console.WriteLine("Close database connection");
        return output;
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
        throw; // Ensure the exception is propagated
    }
}

W tym przykładzie, jeśli wystąpi wyjątek, połączenie z bazą danych nie zostaje prawidłowo zamknięte, co prowadzi do potencjalnych wycieków zasobów. Dodając blok try-catch, można upewnić się, że połączenie jest zamknięte, nawet jeśli wystąpi wyjątek.

Używanie bloku finally

Tim wprowadza blok finally, który zapewnia, że pewien kod jest wykonywany niezależnie od tego, czy wyjątek wystąpi, czy nie. Jest to szczególnie przydatne do sprzątania zasobów, takich jak zamykanie połączeń z bazą danych.

public int GrandparentMethod(int position)
{
    try
    {
        Console.WriteLine("Open database connection");
        int output = ParentMethod(position);
        return output;
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
        throw; // Re-throw the exception to ensure it's handled by a higher-level handler
    }
    finally
    {
        Console.WriteLine("Close database connection");
    }
}
public int GrandparentMethod(int position)
{
    try
    {
        Console.WriteLine("Open database connection");
        int output = ParentMethod(position);
        return output;
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
        throw; // Re-throw the exception to ensure it's handled by a higher-level handler
    }
    finally
    {
        Console.WriteLine("Close database connection");
    }
}

Blok finally działa po blokach try i catch, zapewniając, że połączenie jest zamknięte, nawet jeśli wystąpi wyjątek.

Instrukcja throw

Tim wyjaśnia znaczenie ponownego wyrzucania wyjątków, aby przekazywać je w górę stosu wywołań. Pozwala to wysoko poziomowym obsługiwaczom na odpowiednią obróbkę wyjątków.

catch (Exception ex)
{
    Console.WriteLine(ex.Message);
    throw; // Re-throws the exception to be handled by the calling method
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
    throw; // Re-throws the exception to be handled by the calling method
}

Ponowne wyrzucenie wyjątku za pomocą instrukcji throw; zapewnia, że pełen ślad stosu jest zachowany, dostarczając wartościowy kontekst do debugowania.

Prawidłowe przekazywanie wyjątków w górę

Tim demonstruje, jak wyjątki przepływają przez stos wywołań. Każda metoda sprawdza istnienie bloku try-catch i albo obsługuje wyjątek, albo przekazuje go w górę do wywołującej metody.

try
{
    int result = demo.GrandparentMethod(4); // This will cause an IndexOutOfRangeException
    Console.WriteLine($"The value at the given position is {result}");
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
    Console.WriteLine(ex.StackTrace);
}
try
{
    int result = demo.GrandparentMethod(4); // This will cause an IndexOutOfRangeException
    Console.WriteLine($"The value at the given position is {result}");
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
    Console.WriteLine(ex.StackTrace);
}

W tym przykładzie metoda GrandparentMethod łapie wyjątek, loguje go i ponownie go wyrzuca. Blok try-catch na najwyższym poziomie w aplikacji konsolowej następnie obsługuje wyjątek i wyświetla wiadomość o błędzie oraz ślad stosu.

Powszechne błędy w obsłudze wyjątków

Tim wyróżnia kilka powszechnych błędów, które programiści popełniają podczas obsługi wyjątków:

  1. Używanie throw ex;:
  • Zmiana śladu stosu i utrata wartościowego kontekstu.

  • Przykład:
    catch (Exception ex)
    {
        // Incorrect
        throw ex; // Rewrites stack trace
    }
    catch (Exception ex)
    {
        // Incorrect
        throw ex; // Rewrites stack trace
    }
  1. Wyrzucanie nowego wyjątku:
  • Tworzenie nowego wyjątku z niestandardowym komunikatem, ale utrata oryginalnego śladu stosu.

  • Przykład:
    catch (Exception ex)
    {
        // Incorrect
        throw new Exception("I blew up");
    }
    catch (Exception ex)
    {
        // Incorrect
        throw new Exception("I blew up");
    }

Tworzenie nowego wyjątku bez utraty oryginalnego śladu stosu

Tim wyjaśnia, jak stworzyć nowy wyjątek, zachowując oryginalny ślad stosu. Może to być przydatne, gdy chcesz dostarczyć bardziej znaczący komunikat o błędzie lub inny typ wyjątku, a jednocześnie zachować kontekst oryginalnego błędu.

catch (Exception ex)
{
    throw new ArgumentException("You passed in bad data", ex);
}
catch (Exception ex)
{
    throw new ArgumentException("You passed in bad data", ex);
}

Przekazując oryginalny wyjątek (ex) jako wewnętrzny wyjątek, zachowujesz oryginalny ślad stosu, co jest kluczowe podczas debugowania.

Zachowanie informacji o śladzie stosu

Tim demonstruje, jak uzyskać dostęp do wiadomości oryginalnego wyjątku i śladu stosu podczas tworzenia nowego wyjątku.

catch (Exception ex)
{
    Console.WriteLine("You passed in bad data");
    Console.WriteLine(ex.StackTrace);
    throw new ArgumentException("You passed in bad data", ex);
}
catch (Exception ex)
{
    Console.WriteLine("You passed in bad data");
    Console.WriteLine(ex.StackTrace);
    throw new ArgumentException("You passed in bad data", ex);
}

To zapewnia, że wyjątek wyrzucony w górę stosu zawiera zarówno nowy komunikat, jak i szczegóły oryginalnego wyjątku.

Przechodzenie przez wewnętrzne wyjątki

Tim dostarcza metodę do przechodzenia przez wszystkie wewnętrzne wyjątki, aby wydobyć ich wiadomości i ślady stosu.

catch (Exception ex)
{
    Exception inner = ex;
    while (inner != null)
    {
        Console.WriteLine(inner.StackTrace);
        inner = inner.InnerException;
    }
    throw;
}
catch (Exception ex)
{
    Exception inner = ex;
    while (inner != null)
    {
        Console.WriteLine(inner.StackTrace);
        inner = inner.InnerException;
    }
    throw;
}

Pętla ta iteruje przez każdy wewnętrzny wyjątek, drukując jego ślad stosu, zapewniając, że wszystkie poziomy wyjątków są uwzględnione.

Obsługa różnych wyjątków różnie

Tim omawia, jak obsługiwać różne typy wyjątków za pomocą wielu bloków catch. Pozwala to na specyficzną obsługę w zależności od typu wyjątku.

try
{
    // Code that might throw an exception
}
catch (ArgumentException ex)
{
    Console.WriteLine("You gave us bad information. Bad user!");
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
    Console.WriteLine(ex.StackTrace);
}
try
{
    // Code that might throw an exception
}
catch (ArgumentException ex)
{
    Console.WriteLine("You gave us bad information. Bad user!");
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
    Console.WriteLine(ex.StackTrace);
}

W tym przykładzie wyjątek ArgumentException jest obsługiwany specjalnie poprzez wydruk niestandardowego komunikatu, podczas gdy wszystkie inne wyjątki są obsługiwane przez ogólny obsługiwacz drukujący komunikat wyjątku i ślad stosu.

Csharp Exception Handling 2 related to Obsługa różnych wyjątków różnie

Znaczenie kolejności w wielu blokach catch

Tim podkreśla znaczenie kolejności przy używaniu wielu bloków catch. Najbardziej specyficzne wyjątki powinny być łapane jako pierwsze, a następnie bardziej ogólne wyjątki.

try
{
    // Code that might throw an exception
}
catch (ArgumentException ex)
{
    Console.WriteLine("You gave us bad information. Bad user!");
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
    Console.WriteLine(ex.StackTrace);
}
try
{
    // Code that might throw an exception
}
catch (ArgumentException ex)
{
    Console.WriteLine("You gave us bad information. Bad user!");
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
    Console.WriteLine(ex.StackTrace);
}

Jeśli bardziej ogólny blok catch pojawi się przed specyficznym, złapie wszystkie wyjątki, a specyficzny blok nigdy nie zostanie osiągnięty, co prowadzi do błędów kompilacji.

Wnioski

Zaawansowany przewodnik wideo Tima Coreya na temat obsługi wyjątków w C# obejmuje kluczowe techniki tworzenia nowych wyjątków, zachowywania śladów stosu i efektywnego używania wielu bloków catch. Przestrzegając jego najlepszych praktyk, programiści mogą tworzyć odporne aplikacje, które obsługują wyjątki elegancko i dostarczają wartościowe informacje do debugowania.

Hero Worlddot related to Obsługa wyjątków w C#
Hero Affiliate related to Obsługa wyjątków w C#

Zarabiaj więcej, dzieląc się tym, co kochasz

Tworzysz treści dla deweloperów pracujących z .NET, C#, Java, Python, czy Node.js? Zamień swoją wiedzę specjalistyczną na dodatkowy dochód!

Zespol wsparcia Iron

Jestesmy online 24 godziny, 5 dni w tygodniu.
Czat
Email
Zadzwon do mnie