Zpracování vstupně-výstupních chyb v .NET

Kromě výjimek, které mohou být vyvolány v jakémkoli volání metody (například OutOfMemoryException při zatížení systému nebo NullReferenceException kvůli chybě programátora), mohou metody systému souborů .NET vyvolat následující výjimky:

Mapování kódů chyb na výjimky

Vzhledem k tomu, že systém souborů je prostředek operačního systému, vstupně-výstupní metody v rozhraní .NET Core i rozhraní .NET Framework zabalí volání základního operačního systému. Pokud dojde k V/V chybě v kódu spuštěného operačním systémem, operační systém vrátí informace o chybě do V/V metody .NET. Metoda pak přeloží informace o chybě, obvykle ve formě kódu chyby, do typu výjimky .NET. Ve většině případů to dělá tak, že přímo přeloží kód chyby do odpovídajícího typu výjimky; neprovádí žádné zvláštní mapování chyby na základě kontextu volání metody.

Například v operačním systému Windows volání metody, která vrací kód ERROR_FILE_NOT_FOUND chyby (nebo 0x02) mapuje na , FileNotFoundExceptiona kód ERROR_PATH_NOT_FOUND chyby (nebo 0x03) mapuje na DirectoryNotFoundException.

Přesné podmínky, za kterých operační systém vrací konkrétní kódy chyb, jsou však často nezdokumentovány nebo špatně zdokumentované. V důsledku toho může dojít k neočekávaným výjimkám. Například vzhledem k tomu, že pracujete s adresářem místo souboru, byste očekávali, že poskytnutí neplatné cesty k DirectoryInfo adresáři konstruktoru DirectoryNotFoundExceptionvyvolá . Může však také vyvolat FileNotFoundException.

Zpracování výjimek v vstupně-výstupních operacích

Kvůli závislosti na operačním systému můžou identické podmínky výjimek (například v našem příkladu nenalezena chyba adresáře) vést k vyvolání vstupně-výstupní metody vyvolání jakékoli z jedné ze tříd výjimek vstupně-výstupních operací. To znamená, že při volání rozhraní API pro vstupně-výstupní operace by měl být váš kód připravený na zpracování většiny nebo všech těchto výjimek, jak je znázorněno v následující tabulce:

Typ výjimky .NET Core/.NET 5 nebo novější .NET Framework
IOException Ano Ano
FileNotFoundException Ano Ano
DirectoryNotFoundException Ano Ano
DriveNotFoundException Ano Ano
PathTooLongException Ano Ano
OperationCanceledException Ano Ano
UnauthorizedAccessException Ano Yes
ArgumentException .NET Core 2.0 a starší Yes
NotSupportedException Ne Ano
SecurityException No Pouze omezený vztah důvěryhodnosti

Zpracování ioException

Jako základní třída výjimek v System.IO oboru názvů je vyvolán také pro jakýkoli kód chyby, IOException který se nemapuje na předdefinovaný typ výjimky. To znamená, že ji může vyvolat jakákoli vstupně-výstupní operace.

Důležité

Vzhledem k tomu IOException , že je základní třídou ostatních typů výjimek v System.IO oboru názvů, měli byste zpracovat v catch bloku po zpracování ostatních výjimek souvisejících s vstupně-výstupními operacemi.

Kromě toho, počínaje .NET Core 2.1, ověření kontroluje správnost cesty (například za účelem zajištění, že neplatné znaky nejsou přítomné v cestě) byly odebrány a modul runtime vyvolá výjimku mapovanou z kódu chyby operačního systému místo z vlastního ověřovacího kódu. Nejpravděpodobnější výjimkou, která se v tomto případě vyvolá, je IOException, i když jakýkoli jiný typ výjimky může být vyvolán.

Všimněte si, že v kódu zpracování výjimek byste měli vždy zpracovat IOException poslední. Jinak, protože se jedná o základní třídu všech ostatních výjimek vstupně-výstupních operací, bloky catch odvozených tříd nebudou vyhodnoceny.

V případě IOException, můžete získat další informace o chybě z IOException.HResult vlastnost. Chcete-li převést hodnotu HResult na kód chyby Win32, odstraňte horních 16 bitů 32bitové hodnoty. Následující tabulka uvádí kódy chyb, které mohou být zabaleny do .IOException

Hodnota HResult Konstanta Popis
ERROR_SHARING_VIOLATION 32 Název souboru chybí nebo se používá soubor nebo adresář.
ERROR_FILE_EXISTS 80 Soubor již existuje.
ERROR_INVALID_PARAMETER 87 Argument zadaný metodě je neplatný.
ERROR_ALREADY_EXISTS 183 Soubor nebo adresář již existuje.

Můžete je zpracovat pomocí When klauzule v příkazu catch, jak ukazuje následující příklad.

using System;
using System.IO;
using System.Text;

class Program
{
    static void Main()
    {
        var sw = OpenStream(@".\textfile.txt");
        if (sw is null)
            return;
        sw.WriteLine("This is the first line.");
        sw.WriteLine("This is the second line.");
        sw.Close();
    }

    static StreamWriter? OpenStream(string path)
    {
        if (path is null)
        {
            Console.WriteLine("You did not supply a file path.");
            return null;
        }

        try
        {
            var fs = new FileStream(path, FileMode.CreateNew);
            return new StreamWriter(fs);
        }
        catch (FileNotFoundException)
        {
            Console.WriteLine("The file or directory cannot be found.");
        }
        catch (DirectoryNotFoundException)
        {
            Console.WriteLine("The file or directory cannot be found.");
        }
        catch (DriveNotFoundException)
        {
            Console.WriteLine("The drive specified in 'path' is invalid.");
        }
        catch (PathTooLongException)
        {
            Console.WriteLine("'path' exceeds the maximum supported path length.");
        }
        catch (UnauthorizedAccessException)
        {
            Console.WriteLine("You do not have permission to create this file.");
        }
        catch (IOException e) when ((e.HResult & 0x0000FFFF) == 32)
        {
            Console.WriteLine("There is a sharing violation.");
        }
        catch (IOException e) when ((e.HResult & 0x0000FFFF) == 80)
        {
            Console.WriteLine("The file already exists.");
        }
        catch (IOException e)
        {
            Console.WriteLine($"An exception occurred:\nError code: " +
                              $"{e.HResult & 0x0000FFFF}\nMessage: {e.Message}");
        }
        return null;
    }
}
Imports System.IO

Module Program
    Sub Main(args As String())
        Dim sw = OpenStream(".\textfile.txt")
        If sw Is Nothing Then Return

        sw.WriteLine("This is the first line.")
        sw.WriteLine("This is the second line.")
        sw.Close()
    End Sub

    Function OpenStream(path As String) As StreamWriter
        If path Is Nothing Then
            Console.WriteLine("You did not supply a file path.")
            Return Nothing
        End If

        Try
            Dim fs As New FileStream(path, FileMode.CreateNew)
            Return New StreamWriter(fs)
        Catch e As FileNotFoundException
            Console.WriteLine("The file or directory cannot be found.")
        Catch e As DirectoryNotFoundException
            Console.WriteLine("The file or directory cannot be found.")
        Catch e As DriveNotFoundException
            Console.WriteLine("The drive specified in 'path' is invalid.")
        Catch e As PathTooLongException
            Console.WriteLine("'path' exceeds the maximum supported path length.")
        Catch e As UnauthorizedAccessException
            Console.WriteLine("You do not have permission to create this file.")
        Catch e As IOException When (e.HResult And &h0000FFFF) = 32
            Console.WriteLine("There is a sharing violation.")
        Catch e As IOException When (e.HResult And &h0000FFFF) = 80
            Console.WriteLine("The file already exists.")
        Catch e As IOException
            Console.WriteLine($"An exception occurred:{vbCrLf}Error code: " +
                              $"{e.HResult And &h0000FFFF}{vbCrLf}Message: {e.Message}")
        End Try
        Return Nothing
    End Function
End Module

Viz také