Wyjątki

Wyjątki #

Implementacja wyjątków w C# jest bardzo podobna do tej w C++. Składniowo jest niemalże identycznie, z kilkoma nowymi możliwościami w C#. Zmianą jest to, że w C# możemy rzucać jedynie obiektami dziedziczącymi po System.Exception. W C++ mogliśmy rzucać w zasadzie dowolnym obiektem, czego się i tak nie robi w praktyce.

Łapanie wyjątków #

Tak jak w C++, żeby złapać wyjątek otaczamy kod który może rzucić wyjątkiem w blok try i dołączamy do niego bloki catch z obsługą sytuacji wyjątkowych. W przypadku wyjątku, program szuka odpowiedniego bloku catch od góry do dołu.

try
{
    string text = File.ReadAllText("file.txt");
    int number = int.Parse(text);
}
catch (FileNotFoundException ex)
{
    Console.WriteLine($"File not found: {ex}");
}
catch (FormatException ex)
{
    Console.WriteLine($"Invalid format: {ex}");
}
catch (Exception ex) // This will catch any exception
{
    Console.WriteLine($"Unexpected exception: {ex}");
}

Zgłaszanie wyjątków #

Żeby zgłosić wyjątek używamy instrukcji throw po której następuje obiekt wyjątku, zazwyczaj tworzony w miejscu operatorem new. Sam typ wyjątku po części dokumentuje sytuację wyjątkową, ale warto również doprecyzować co się stało podając dodatkową wiadomość w parametrze konstruktora.

public static void ValidateInput(string input, int maxLength)
{
    if (string.IsNullOrWhiteSpace(input))
    {
        throw new ArgumentNullException(nameof(input), "Input cannot be null or empty.");
    }

    if (input.Length > maxLength)
    {
        throw new ArgumentOutOfRangeException(nameof(input), $"Input exceeds the maximum length of {maxLength} characters.");
    }
}

Przerzucenie wyjątku #

W bloku catch możemy przekazać dalej wyjątek jednocześnie częściowo na niego reagując (np. przez zalogowanie błędu), służy do tego instrukcja throw bez żadnego parametru.

try
{
    ValidateInput("Hello, world", 16);
}
catch (ArgumentNullException e)
{
    Console.WriteLine(e);
}
catch (ArgumentOutOfRangeException e)
{
    Console.WriteLine(e);
}
catch (Exception e)
{
    Console.WriteLine(e);
    throw;
}

Filtry wyjątków #

Do bloków catch możemy doklejać klauzulę when w której możemy sprecyzować dodatkowe warunki, które muszą być spełnione:

try
{
    throw new HttpRequestException("Resource not found", null, System.Net.HttpStatusCode.NotFound);
}
catch (HttpRequestException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
{
    Console.WriteLine("Resource not found.");
    Console.WriteLine(ex);
}
catch (HttpRequestException ex)
{
    Console.WriteLine(ex);
}

Własne typy wyjątków #

Biblioteka standardowa dostarcza całkiem pokaźny wachlarz wbudowanych typów wyjątków. Czasami jednak chcemy bardziej szczegółowe typy wyjątków lepiej opisujące sytuację. Dzięki temu kod staje się bardziej czytelny, a obsługa błędów bardziej precyzyjna. Zazwyczaj reimplementuje się trzy najczęściej używane konstruktory, ale oczywiście można w takiej klasie dodawać własne konstruktory i pola.

public class InvalidOrderException : Exception
{
    public InvalidOrderException() 
    {
    }

    public InvalidOrderException(string message)
        : base(message)
    {
    }

    public InvalidOrderException(string message, Exception inner)
        : base(message, inner)
    {
    }
}

W końcu #

Blok finally może zostać dołączony do bloku try-catch. Kod w takim bloku wykona się zawsze, niezależnie od okoliczności w jakich wychodzimy z bloku try. Głównym zastosowaniem bloku finally jest zwalnianie zasobów takich jak: strumienie plików, połączenie z bazą danych, zasoby sieciowe.

FileStream? fs = null;
try
{
    fs = new FileStream("file.txt", FileMode.Open);
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
}
finally
{
    fs?.Dispose();
}
comments powered by Disqus