Sdílet prostřednictvím


Použití výjimek

V jazyce C# se chyby v programu za běhu šíří prostřednictvím programu pomocí mechanismu označovaného jako výjimky. Výjimky jsou vyvolány kódem, který narazí na chybu a zachytil kód, který může chybu opravit. Výjimky můžou vyvolat modul runtime .NET nebo kód v programu. Jakmile dojde k vyvolání výjimky, propaguje se po zásobníku volání, dokud není nalezen příkaz pro výjimku catch. Nezachycené výjimky zpracovává obecný zpracovatel výjimek poskytovaný systémem, která zobrazuje dialogové okno.

Výjimky jsou reprezentovány třídami odvozenými z Exception. Tato třída identifikuje typ výjimky a obsahuje vlastnosti s podrobnostmi o výjimce. Vyvolání výjimky zahrnuje vytvoření instance odvozené třídy výjimky, volitelně konfigurace vlastností výjimky a následné vyvolání objektu pomocí klíčového throw slova. Například:

class CustomException : Exception
{
    public CustomException(string message)
    {
    }
}
private static void TestThrow()
{
    throw new CustomException("Custom exception in TestThrow()");
}

Po vyvolání výjimky modul runtime zkontroluje aktuální příkaz a zjistí, jestli je v try bloku. Pokud ano, zkontrolují se všechny catch bloky přidružené k try bloku a zkontrolují, jestli můžou výjimku zachytit. Catch bloky obvykle určují typy výjimek; pokud je typ catch bloku stejný typ jako výjimka nebo základní třída výjimky, catch může blok zpracovat metodu. Například:

try
{
    TestThrow();
}
catch (CustomException ex)
{
    System.Console.WriteLine(ex.ToString());
}

Pokud příkaz, který vyvolá výjimku, není v try bloku nebo pokud try blok, který ho uzavírá, nemá žádný odpovídající catch blok, modul runtime zkontroluje metodu, která ho volá, pro příkaz try a bloky catch. Modul runtime pokračuje v zásobníku volání a hledá kompatibilní catch blok. Po nalezení a spuštění bloku catch je řízení předáno dalšímu příkazu po tomto bloku catch.

Příkaz try může obsahovat více než jeden catch blok. První catch příkaz, který dokáže zpracovat výjimku, se spustí. Všechny následující catch příkazy, i když jsou kompatibilní, se ignorují. Uspořádejte bloky zachytávání od nejvíce specifických (nebo nejvíce odvozených) po nejméně specifické. Například:

using System;
using System.IO;

namespace Exceptions
{
    public class CatchOrder
    {
        public static void Main()
        {
            try
            {
                using (var sw = new StreamWriter("./test.txt"))
                {
                    sw.WriteLine("Hello");
                }
            }
            // Put the more specific exceptions first.
            catch (DirectoryNotFoundException ex)
            {
                Console.WriteLine(ex);
            }
            catch (FileNotFoundException ex)
            {
                Console.WriteLine(ex);
            }
            // Put the least specific exception last.
            catch (IOException ex)
            {
                Console.WriteLine(ex);
            }
            Console.WriteLine("Done");
        }
    }
}

Před spuštěním catch bloku modul runtime kontroluje finally bloky. Finally bloky umožňují programátoru vyčistit jakýkoli nejednoznačný stav, který by mohl být ponechán z přerušeného try bloku, a uvolnit veškeré externí prostředky (například grafické popisovače, připojení k databázi nebo streamy souborů), aniž bychom čekali na to, až modul runtime dokončí uvolnění objektů. Například:

static void TestFinally()
{
    FileStream? file = null;
    //Change the path to something that works on your machine.
    FileInfo fileInfo = new System.IO.FileInfo("./file.txt");

    try
    {
        file = fileInfo.OpenWrite();
        file.WriteByte(0xF);
    }
    finally
    {
        // Closing the file allows you to reopen it immediately - otherwise IOException is thrown.
        file?.Close();
    }

    try
    {
        file = fileInfo.OpenWrite();
        Console.WriteLine("OpenWrite() succeeded");
    }
    catch (IOException)
    {
        Console.WriteLine("OpenWrite() failed");
    }
}

Pokud WriteByte() vyvoláte výjimku, kód ve druhém try bloku, který se pokusí znovu otevřít soubor, selže, pokud file.Close() není volána, a soubor zůstane uzamčený. Jelikož se bloky finally spouští i tehdy, když je vyvolána výjimka, blok finally v předchozím příkladu umožňuje správné uzavření souboru a pomáhá vyhnout se chybě.

Pokud se v zásobníku volání po vyvolání výjimky nenajde žádný kompatibilní catch blok, dojde k jedné ze tří věcí:

  • Pokud je výjimka ve finalizátoru, finalizátor je přerušen a, pokud existuje, je volán jeho základní finalizátor.
  • Pokud zásobník volání obsahuje statický konstruktor nebo inicializátor statického pole, je vyvolána výjimka TypeInitializationException, přičemž původní výjimka je přiřazena k vlastnosti InnerException nové výjimky.
  • Pokud je dosaženo začátku vlákna, vlákno se ukončí.